Многопоточность: WaitAll не ждет, как ожидалось

У меня есть поток, который вызывает два отдельных потока для выполнения некоторой работы. Всякий раз, когда какое-либо из заданий завершено, вызывается Waithandle.Set(0, и в конце родительского рабочего потока я хотел, чтобы WaitAll для обоих были завершены, прежде чем я продолжу. Но priceA() все еще появляется сначала, а затем PriceB ().

new Thread(() =>
                           {
                               new Thread(() =>
                               {
                                   PriceA = _service.GetPriceA();
                                   _waithandle[0].Set();
                               }).Start();

                               new Thread(() =>
                               {
                                   PriceB = _service.GetPriceB();
                                   _waithandle[1].Set();
                               }).Start();

                               WaitHandle.WaitAll(_waithandle);
                           }).Start();
Console.WriteLine("Hello");

Что мне не хватает?

Обновление:

private EventWaitHandle[] _waithandle;

Ктор:

 _waithandle[0] = new ManualResetEvent(false);
 _waithandle[1] = new ManualResetEvent(false);

person Houman    schedule 19.02.2011    source источник
comment
Ваш вопрос не ясен. Единственное, что должен сделать ваш WaitAll, — это остановиться до того, как завершится первый новый Thread() (тот, что в строке 1). Поэтому, если вы вставите Console.WriteLine(Hello) после WaitAll, он будет напечатан только ПОСЛЕ завершения обоих GetPrice. Порядок выполнения GetPrices не фиксирован. Они могут выполняться как GetPriceA, GetPriceB или GetPriceB, GetPriceA.   -  person xanatos    schedule 19.02.2011


Ответы (3)


Вы создаете отдельный поток для ожидания... но код после указанного вами оператора будет продолжаться, потому что вы не ожидаете в этом потоке. Другими словами, вы создаете три потока:

  • Поток X: создает потоки A и B, затем ожидает их завершения.
  • Поток A: получает PriceA, а затем устанавливает waitHandle[0]
  • Поток B: получает PriceB, а затем устанавливает waitHandle[1]

Но поток X ничего не делает после ожидания, так какой смысл ждать внутри него?

Кроме того, было бы намного проще просто вызвать Join для дополнительных потоков, которые вы создали. На самом деле, если вам нужно ждать только в «текущем» потоке, вам нужен только один дополнительный поток:

Thread t = new Thread(() => { PriceA = _service.GetPriceA(); });
t.Start();
PriceB = _service.GetPriceB();
t.Join();
// Other code

К тому времени, когда вы дойдете до «другого кода», будут установлены и PriceA, и PriceB. Конечно, здесь отсутствует значительная часть обработки ошибок... но ее легче добавить, когда у вас есть более простая отправная точка, чем ваш нынешний чрезмерно сложный код.

person Jon Skeet    schedule 19.02.2011
comment
Вам нужно сделать это в приложении STA. В противном случае WaitAll не позволит вам скомпилироваться. Но описанное вами поведение — это именно то, чего я пытаюсь добиться. Я только что понял, в чем проблема.... InotifyChangedProperty PriceA и PriceB испортил мои намерения. Теперь я сначала сохраняю их в соответствующем поле, а затем передаю их в реальные свойства после WaitAll. Это работает, как и ожидалось. - person Houman; 19.02.2011
comment
@Kave: WaitAll, безусловно, будет скомпилировать в контексте потока, отличного от STA, но он взорвется во время выполнения. Хотя я бы избегал этого, если это возможно. Если вы используете .NET 4, библиотека параллельных задач еще больше упростит работу... - person Jon Skeet; 19.02.2011
comment
Спасибо Джон, я освежаю свои навыки для интервью на следующей неделе. Это не настоящее приложение. :) Вы правы, TPL упрощает задачу, но мне нужно подготовиться к .NET 3.5. Я также разместил свой ответ ниже на случай, если кто-то еще упустил из виду поведение свойств INotifyChangedproperty(), как в моем случае. - person Houman; 19.02.2011

new Thread(() =>
                           {
                               new Thread(() =>
                               {
                                   _priceA = _service.GetPriceA();
                                   _waithandle[0].Set();
                               }).Start();

                               new Thread(() =>
                               {
                                   _priceB = _service.GetPriceB();
                                   _waithandle[1].Set();
                               }).Start();

                               WaitHandle.WaitAll(_waithandle);
                               PriceA = _priceA;
                               PriceB = _priceB;
                           }).Start();

Это сделало работу для меня. Виновником был INotifyChangedProperty() для PriceA и PriceB, который слишком рано обновил пользовательский интерфейс, что сделало мое ожидание избыточным. Если у кого-то еще есть похожая проблема...

person Houman    schedule 19.02.2011

Вы правильно сбрасываете _waithandle[0] и [1]? Например:

_waithandle[0] = new ManualResetEvent(false);
_waithandle[1] = new ManualResetEvent(false);
person xanatos    schedule 19.02.2011