Thread.Yield против WaitOne - PullRequest
       7

Thread.Yield против WaitOne

0 голосов
/ 26 октября 2018

Насколько я понимаю, Thread.Yield может использоваться вместо WaitOne и ManualResetEvent для сигнализации о потоке.

Хотя я не встречал документ, объясняющий точное поведение WaitOne за кулисами, я предполагаю, что он переводит поток в состояние ожидания и говорит планировщику ОС проверять, установлен ли ManualResetEvent каждый раз это очередь этого потока в очереди. Если не установлен, планировщик не выполняет переключение контекста и переходит к другому потоку. Если установлено, планировщик переводит поток в рабочее состояние, поэтому код после WaitOne начинает выполняться.

С другой стороны, использование Thread.Yield приведет к переключению контекста независимо от состояния ManualResetEvent и выполнит проверку позже.

Правильно ли мое понимание? Есть ли документ, который объясняет внутреннюю работу WaitOne?

P.S .: Вот примеры кодов обеих версий для демонстрационных целей:

var signal = new ManualResetEvent(false);
new Thread(() =>
{
    Console.WriteLine("Waiting for signal...");
    signal.WaitOne();
    signal.Dispose();
    Console.WriteLine("Got signal!");
}).Start();
Thread.Sleep(2000);
signal.Set(); // "Open" the signal

bool signal = false;
new Thread(() =>
{
    Debug.WriteLine("Waiting for signal...");
    while(signal == false)
    {
        Thread.Yield();
    }
    Debug.WriteLine("Got signal!");
}).Start();
Thread.Sleep(2000);
signal = true; ; // "Open" the signal

1 Ответ

0 голосов
/ 27 октября 2018

Во-первых, комментарии Ганса совершенно верны: вы плохо придумываете свое собственное вращение. Не делай этого!

Тем не менее, ваш вопрос не о том, должен ли повторно реализовать WaitOne, а, скорее, о том, как WaitOne был реализован людьми, у которых его не было, потому что он еще не был написан . Вполне разумно рассмотреть этот вопрос; такие функции не магические и были реализованы людьми, так как же они это сделали?

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

Во-первых, вы правы, отметив, что Thread.Yield является более примитивной операцией, и поэтому ее можно использовать как часть стратегии для создания операции более высокого уровня, такой как WaitOne. Но на практике это, вероятно, не будет использоваться в наивной манере, которую вы описываете, по нескольким причинам:

  • Thread.Yield действительно создает барьер, но из кода на 100% не очевидно, что чтение bool не было опущено или что запись не может быть отложена. Мы хотели бы сделать очень, очень уверенным, что запись bool была подхвачена, и что введение барьера не ухудшило производительность.

  • Thread.Yield уступает управление любому готовому потоку на текущем процессоре . Что произойдет, если на текущем процессоре нет готового потока? Возможно, обдумайте это. Что удерживает этот код от нагрева всего процессора? Что произойдет, если поток, который собирается выполнить запись, находится на другом процессоре? Каковы все возможные сценарии, связанные с голоданием нити и т. Д.

  • Рассмотрим этот сценарий: у нас есть многопоточный процессор с тремя потоками, Alpha, Bravo и Charlie, а Alpha и Bravo в настоящее время выполняются в CPU. Альфа имеет в своем кванте 10 миллионов наносекунд, она видит, что флаг ложен, и уступает Чарли остаток своего кванта Спустя одну наносекунду, Браво устанавливает флаг. Мы просто взяли на себя полную стоимость переключения контекста, и Alpha отказалась от возможности выполнить десять миллионов наносекунд работы! «Альфе» было бы лучше, если бы она ждала несколько десятков из своих десяти миллионов наносекунд, а не взяла на себя огромные затраты на переключение контекста. Это те сценарии, которые вы должны учитывать при разработке нового многопоточного примитива . Просто получить правильный поток управления не достаточно хорошо; вы принимаете неверное решение на горячем пути и можете снизить производительность в тысячи или миллионы раз.

  • и т. Д.

Но подождите, становится хуже. Есть ли более тонкие проблемы, которые WaitOne должен решить?

Конечно. У CLR есть инварианты, которые он должен поддерживать. Вы должны помнить, что CLR был изначально изобретен как расширение COM , и базовая реализация глубоко внедрена в мир COM. В частности, все правила о распределении по-прежнему применяются. WaitOne эффективно усыпляет поток, но это может привести к проблемам с маршаллером. Статья Криса Брамма об этом особенно страшна и выявляет:

https://blogs.msdn.microsoft.com/cbrumme/2004/02/02/apartments-and-pumping-in-the-clr/

Прочтите это, посмотрите, сможете ли вы понять все это. Я читал его десятки раз с 2004 года, и раньше я был профессиональным программистом COM, и я получаю, возможно, 80% этого. Это сложная вещь, и если вы ее не понимаете, вы не можете написать правильную реализацию WaitOne, которая соответствует потребностям CLR.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...