Очистка потоков, ссылающихся на объект, при удалении объекта (в C ++) - PullRequest
2 голосов
/ 09 марта 2010

У меня есть объект (клиент * клиент), который запускает несколько потоков для обработки различных задач (таких как обработка входящих данных). Потоки запускаются так:

// Start the thread that will process incoming messages and stuff them into the appropriate queues.
mReceiveMessageThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)receiveRtpMessageFunction, this, 0, 0);

Все эти потоки имеют ссылки на исходный объект, например:

  // Thread initialization function for receiving RTP messages from a newly connected client.
  static int WINAPI receiveRtpMessageFunction(LPVOID lpClient)
  {
     LOG_METHOD("receiveRtpMessageFunction");
     Client * client = (Client *)lpClient;
     while(client ->isConnected())
     {
        if(client ->receiveMessage() == ERROR)
        {
           Log::log("receiveRtpMessageFunction Failed to receive message");
        }
     }

     return SUCCESS;
  }

Периодически объект «Клиент» удаляется (по разным веским и достаточным причинам). Но когда это происходит, потоки обработки, которые все еще имеют ссылки на (теперь удаленный) объект, генерируют исключения того или иного рода при попытке доступа к функциям-членам этого объекта.

Так что я уверен, что есть стандартный способ справиться с этой ситуацией, но я не смог найти правильный подход. Я не хочу просто прекратить поток, поскольку это не позволяет очистить ресурсы. Я не могу установить свойство для объекта, так как именно свойства объекта становятся недоступными.

Мысли о том, как лучше всего с этим справиться?

Ответы [ 5 ]

2 голосов
/ 09 марта 2010

Я бы решил эту проблему, введя счетчик ссылок на ваш объект. Рабочий поток будет содержать ссылку, как и создатель объекта. Вместо использования delete вы уменьшаете счетчик ссылок, и тот, кто выбрасывает последнюю ссылку, будет фактически вызывать delete.

Вы можете использовать существующие механизмы подсчета ссылок (shared_ptr и т. Д.), Или вы можете использовать свои собственные с Win32 API InterlockedIncrement() и InterlockedDecrement() или аналогичными (возможно, счетчик ссылок volatile DWORD начинается с 1 ...).

Единственное, чего не хватает, так это того, что когда основной поток освобождает свою ссылку, он должен дать сигнал рабочему потоку удалить свою собственную ссылку. Один из способов сделать это - событие ; Вы можете переписать цикл рабочего потока как вызовы WaitForMultipleObjects(), и когда определенное событие сигнализируется, это означает, что рабочий поток должен очистить и удалить ссылку.

1 голос
/ 09 марта 2010

У вас мало возможностей из-за запущенных потоков.

Никакая комбинация shared_ptr + weak_ptr не может спасти вас ... вы можете вызвать метод объекта, когда он действителен, а затем заказать его уничтожение (используя только shared_ptr).

Единственное, что я могу себе представить, - это сначала завершить различные процессы, а затем уничтожить объект. Таким образом вы гарантируете, что каждый процесс завершится корректно, при необходимости убирая свой собственный беспорядок (и для этого может потребоваться объект).

Это означает, что вы не можете удалить объект из-под контроля, так как вы должны сначала выполнить повторную синхронизацию с теми, кто его использует, и что вам нужна некоторая обработка событий для части синхронизации (так как вы в основном хотите сказать потоки надо останавливать, а не ждать их бесконечно).

Я оставляю вам часть синхронизации, есть много альтернатив (события, флаги и т. Д.), И у нас недостаточно данных.

Вы можете справиться с фактической очисткой либо самого деструктора, либо перегрузив различные операции delete, в зависимости от того, что вам подходит.

0 голосов
/ 09 марта 2010

Это можно сделать, передав слабый указатель (буст) на потоки.

0 голосов
/ 09 марта 2010

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

0 голосов
/ 09 марта 2010

Вам потребуется другой объект состояния, который потоки могут проверить, чтобы убедиться, что «клиент» все еще действителен.

Одним из вариантов является инкапсуляция вашей клиентской ссылки в некоторый другой объект, который остается постоянным, и предоставление ссылки на этот объект из ваших потоков.

...