Что такое эквивалент С# для MsgWaitForMultipleObjects?

У меня есть форма Windows с ListView в режиме отчета. Для каждого элемента в представлении мне нужно выполнить длительную операцию, результатом которой является число.

Я бы сделал это в native win32, чтобы создать рабочий поток для каждого элемента (наивно, конечно, я не буду создавать неограниченное количество потоков), а затем MsgWaitForMultipleObjects() в массиве ручек потоков. По мере завершения каждого вычисления сигнал потоков и основной поток пользовательского интерфейса просыпаются и обновляются. В то же время мы накачиваем сообщения, чтобы поток пользовательского интерфейса оставался отзывчивым.

Может ли кто-нибудь представить пример того, как это может работать на С#? Я посмотрел на объект Monitor, и, похоже, это не то, что я хочу - или он накачивает сообщения при блокировке?

Спасибо.

Изменить: Кажется, что WaitHandler.WaitAny() может действительно накачать сообщения. См. cbrumme трактат о пересылке сообщений в среде CLR.

Ответ 1

Долгожданный активный объект, я думаю, лучший выбор в вашем случае. Основной поток вызывает прокси (активного объекта). Прокси преобразует метод вызова в сообщение, и это сообщение переходит в очередь. Прокси возвращает вызывающему объекту будущий объект (это ссылка на будущий результат). Диспетчер делит сообщения один за другим и выполняет свою задачу в другом потоке (рабочий поток). Когда рабочий поток завершает задачу, он обновляет результат будущего объекта или вызывает метод обратного вызова (например, для обновления вашего пользовательского интерфейса). Dispather может иметь много рабочих потоков для одновременного выполнения одной задачи.

Вы можете увидеть это article (с образцом) относительно долгого активного шаблона объекта.

Ответ 2

Создайте свой основной поток для менеджера. Вы можете использовать BackgroundWorker для этого. Этот поток менеджера запускает рабочий поток для каждого элемента в ListView. Это позволит вашему пользовательскому интерфейсу продолжать отвечать на ввод пользователя без зависания при обработке фоновых потоков.

Теперь проблема заключается в том, как дождаться завершения каждого рабочего потока. К сожалению, я не смог найти способ получить дескриптор потока для объектов System.Threading.Thread. Я не говорю, что нет способа сделать это; Я просто его не нашел. Другим осложняющим аспектом этого является то, что класс System.Threading.Thread запечатан, поэтому мы не можем извлечь из него какой-то "дескриптор".

Здесь я использую ManualResetEvent.

Предположим, что каждый рабочий поток - это просто поток ThreadPool. Управление BackgroundWorker создает объект ManualResetEvent для каждого элемента в ListView. Когда BackgroundWorker запускает поток ThreadPool, передайте ManualResetEvent в качестве аргумента функции QueueUserWorkItem. Затем, перед тем, как выйдет поток ThreadPool, установите объект ManualResetEvent.

Затем поток BackgroundWorker помещает все объекты ManualResetEvent в массив и ждет этого массива с помощью WaitHandle.WaitXXX functions. По мере завершения каждого потока вы можете использовать события BackgroundWorker для обновления пользовательского интерфейса, или вы можете использовать метод Control.Invoke() для обновления пользовательского интерфейса (см. Ответ Марка Гравелла здесь).

Надеюсь, что это поможет.