Как я уже говорил ранее, то, что вы делаете, совершенно бессмысленно, вы можете также не использовать потоки, поскольку вы запускаете поток, а затем ждете завершения потока, прежде чем делать что-либо еще.
Вы предоставляете очень мало информации о вашем CEvent, но ваши объекты WaitForSingleObject ожидают, когда поток войдет в сигнальное состояние (т. Е. Они выйдут).
Поскольку MyCommonFunction - это место, где происходит потенциально небезопасный поток, вы правильно критически разбили область, однако потоки 2 и потоки 3 не работают одновременно. Удалите WaitForSingleObject из MyThreadFunction3, и тогда у вас оба будут работать одновременно потокобезопасным образом, благодаря критическому разделу.
Тем не менее, это все еще немного бессмысленно, так как оба потока будут тратить большую часть своего времени в ожидании освобождения критической секции. В общем случае вы хотите структурировать потоки так, чтобы они имели очень мало нужных для попадания в критические секции, а затем, когда они попадают в критические секции, ударяют по ним только в течение очень короткого времени (т.е. не в подавляющем большинстве времени обработки функции) .
Редактировать :
A Критическая секция работает, говоря, что я держу эту критическую секцию все, что хочет, чтобы оно ожидало. Это означает, что поток 1 входит в критическую секцию и начинает делать то, что ему нужно. Затем появляется поток 2 и говорит: «Я хочу использовать критический раздел». Ядро говорит, что «Поток 1 использует критическую секцию, которую вы должны дождаться своей очереди». Тема 3 приходит и получает то же самое. Потоки 2 и 3 теперь находятся в состоянии ожидания, ожидая освобождения этого критического раздела. Когда Нить 1 заканчивается критической секцией, обе Нити 2 и 3 мчатся, чтобы увидеть, кто первым держит критическую секцию, а когда одна получает ее, другая должна продолжить ожидание.
Теперь в вашем примере, приведенном выше, было бы так много ожидающих критических секций, возможно, что Поток 1 может быть в критической секции, а Поток 2 - в ожидании, прежде чем Потоку 2 будет предоставлена возможность войти в критическую секцию зациклился и снова вошел в него. Это означает, что поток 1 может в конечном итоге выполнить всю свою работу до того, как поток 2 сможет получить доступ к критическому разделу. Поэтому поддержание объема работы, выполняемой в критической секции, по сравнению с остальной частью цикла / функции как можно ниже, поможет одновременно работать потокам. В вашем примере один поток ВСЕГДА будет ожидать другого потока и, следовательно, простое последовательное выполнение этого может на самом деле быть быстрее, поскольку у вас нет накладных расходов на многопоточность ядра.
т.е. чем больше вы избегаете CriticalSections, тем меньше времени теряется для потоков, ожидающих друг друга. Однако они необходимы, так как вам необходимо убедиться, что 2 потока не пытаются одновременно работать с одним и тем же объектом. Некоторые встроенные объекты «атомарные» , которые могут помочь вам в этом, но для неатомарных операций критический раздел является обязательным.
An Событие - это другой тип объекта синхронизации. По сути, событие - это объект, который может быть одним из двух состояний. Сигнализируется или не сигнализируется. Если вы подождете WaitForSingleObject для события «not-signalled», то поток будет переведен в спящий режим до тех пор, пока он не войдет в сигнальное состояние.
Это может быть полезно, когда у вас есть поток, который ДОЛЖЕН ждать, пока другой поток что-то завершит. В общем, хотя вы хотите избегать использования таких объектов синхронизации в максимально возможной степени, поскольку это разрушает параллельность вашего кода.
Лично я использую их, когда у меня есть рабочий поток, ожидающий, когда ему нужно что-то сделать. Поток находится в состоянии ожидания большую часть своего времени, а затем, когда требуется некоторая фоновая обработка, я сигнализирую об этом событии. Затем поток переходит к жизни и делает то, что ему нужно сделать, прежде чем вернуться назад и снова войти в состояние ожидания. Вы также можете пометить переменную как указание, что объект должен выйти. Таким образом, вы можете установить выходную переменную в true и затем сигнализировать ожидающему потоку. Ожидающая нить просыпается и говорит «Я должен выйти», а затем выходит. Имейте в виду, однако, что вам «может» понадобиться барьер памяти , который говорит, что убедитесь, что переменная выхода установлена, прежде чем событие проснется, иначе компилятор может переупорядочить операции. Это может привести к тому, что ваш поток проснется, обнаружив, что переменная выхода не настроена, выполняет свою задачу и затем возвращается в спящий режим. Однако поток, который первоначально отправил сигнал, теперь предполагает, что поток завершился, хотя на самом деле этого не произошло.
Кто бы сказал, что многопоточность была легкой, а? ;)