2009-07-16 2 views
3

У меня есть std :: map, которая используется несколькими потоками для хранения данных. Карта объявляется так:Указатель на значение в std :: map

std::map<int, Call> calls; 

Из каждого потока, я должен получить блокировку мьютекса, получить указатель или ссылку на объект, принадлежащий к этой теме, а затем снять блокировку мьютекса. Я могу изменить объект после этого, потому что каждый объект используется только одним потоком. Как только нить погибнет, соответствующая пара на карте также будет удалена.

Я хотел бы знать, как лучше всего реализовать это. Я думал о двух способах:

1) Я знаю, что это одно может выглядеть безобразно с ума, но до сих пор

std::map<int, Call> calls; 
... 

{ 
    mutex.lock(); 
    Call* callptr = &calls[id]; 
    mutex.unlock(); 

    // use callptr 
} 

или 2) Я думаю, что это один выглядит более разумным

std::map<int, std::auto_ptr<Call> > calls; 

... 

{ 
    mutex.lock(); 
    std::auto_ptr<Call> callptr = map[id]; 
    mutex.unlock(); 

    // use callptr 

    mutex.lock(); 
    map[id] = callptr; 
    mutex.unlock(); 
} 

Нить фактически создаются в другой dll, и у меня нет кода для этого. Эта DLL, которую я пишу сейчас, импортируется этой DLL и используется. Поэтому он должен быть реализован только с помощью std :: map, но может ли кто-нибудь сказать мне, подходит ли один из этих методов или есть способы сделать его более стабильным.

Благодаря

+0

Получение указателя на элемент вызова может работать сейчас, но поскольку вы помещаете фактические экземпляры в объект карты, std :: map может решить внутренне скопировать его в другое место. Это означало бы, что указатель уже недействителен и потенциально может указывать на освобожденную память. На данный момент я не могу придумать решение, но я просто хотел сообщить вам, что это не сработает. – arke

ответ

5

Вы должны использовать итераторы:

mutex.lock(); 

std::map<int, Call>::iterator callptr = calls.find(id); 
callptr->second.foo(); 
... 

mutex.unlock(); 

Ваше первое решение с указателями является проблематичным, поскольку время жизни объекта в карте является неопределенным - он может быть перемещен, когда дерево перебалансированы когда элементы вставлены или удалены.

Ваше второе решение не будет работать вообще, потому что std::auto_ptr не отвечает требованиям, предъявляемым к mapped_type из std::map - в основном потому, что его конструктор копирования и operator= фактически не копировать. Вероятно, вы не получите ошибку компилятора, но во время выполнения вы получите очень странное поведение.

+0

Спасибо за предложение итератора, Павел. И я не знал об умных указателях и контейнерах. – Sahas

+8

Извините, вы ошибаетесь. Время жизни объекта в std :: map начинается, когда оно вставлено и заканчивается, когда оно удалено или карта удалена. Rebalancing изменяет внутренние указатели узлов, но не затрагивает ни ключ, ни значение. – MSalters

2

Не используйте auto_ptr в контейнерах STL. DO NOT. Разработчикам действительно необходимо попробовать и сделать там ошибку компиляции. Стандартные контейнеры имеют тенденцию копировать вещи вокруг. Это очень неправильно с auto_ptr.

3

По мне Thread Local storage - лучший вариант для вас.

Если вам нужны данные, относящиеся к потоку, вы можете использовать локальное хранилище потоков и полностью исключить необходимость блокировки карты и мьютекса.

+0

Да, я знаю, но, к сожалению, я не могу этого сделать, потому что потоки создаются в другой DLL, для которой у меня нет кода! – Sahas

+0

Это не проблема. Вы должны вызывать TlsAlloc один раз за процесс, а не один раз в потоке. – MSalters

Смежные вопросы