Действительно ли эта функция ориентирована на многопотоковое исполнение?

Я изучаю многопоточность, и ради понимания у меня есть wriiten небольшая функция с помощью многопоточности... это хорошо работает. Но я просто хочу знать, безопасно ли тот поток использовать, сделал я следовал корректному правилу.

void CThreadingEx4Dlg::OnBnClickedOk()
{
    //in thread1 100 elements are copied to myShiftArray(which is a CStringArray)
    thread1 = AfxBeginThread((AFX_THREADPROC)MyThreadFunction1,this);
    WaitForSingleObject(thread1->m_hThread,INFINITE);
    //thread2 waits for thread1 to finish because thread2 is going to make use of myShiftArray(in which thread1 processes it first)
    thread2 = AfxBeginThread((AFX_THREADPROC)MyThreadFunction2,this);
    thread3 = AfxBeginThread((AFX_THREADPROC)MyThreadFunction3,this);

}

UINT MyThreadFunction1(LPARAM lparam)
{
    CThreadingEx4Dlg* pthis = (CThreadingEx4Dlg*)lparam;
    pthis->MyFunction(0,100);
    return 0;
}
UINT MyThreadFunction2(LPARAM lparam)
{
    CThreadingEx4Dlg* pthis = (CThreadingEx4Dlg*)lparam;
    pthis->MyCommonFunction(0,20);
    return 0;
}

UINT MyThreadFunction3(LPARAM lparam)
{
    CThreadingEx4Dlg* pthis = (CThreadingEx4Dlg*)lparam;
    WaitForSingleObject(pthis->thread3->m_hThread,INFINITE);
    //here thread3 waits for thread 2 to finish so that thread can continue
    pthis->MyCommonFunction(21,40);
    return 0;
}
void CThreadingEx4Dlg::MyFunction(int minCount,int maxCount)
{

    for(int i=minCount;i<maxCount;i++)
    {
        //assume myArray is a CStringArray and it has 100 elemnts added to it.
        //myShiftArray is a CStringArray -public to the class
        CString temp;
        temp = myArray.GetAt(i);
        myShiftArray.Add(temp);
    }

}

void CThreadingEx4Dlg::MyCommonFunction(int min,int max)
{
    for(int i = min;i < max;i++)
    {
        CSingleLock myLock(&myCS,TRUE);
        CString temp;
        temp = myShiftArray.GetAt(i);
        //threadArray is CStringArray-public to the class
        threadArray.Add(temp);
    }
    myEvent.PulseEvent();

}
1
задан Rob 5 May 2010 в 08:57
поделиться

3 ответа

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

Вы даете очень мало информации о CEvent, но ваши объекты WaitForSingleObject ждут, пока поток не войдет в сигнальное состояние (т. Е. Завершится).

Поскольку MyCommonFunction - это то место, где происходит фактическая потенциально небезопасная для потоков вещь, вы правильно разбили критическую область на разделы, однако потоки 2 и потоки 3 не выполняются одновременно. Удалите объект WaitForSingleObject из MyThreadFunction3, и тогда они будут работать одновременно в потокобезопасном режиме благодаря критическому разделу.

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

Править :

A Критическая секция работает, говоря, что я держу эту критическую секцию, все остальное, что хочет, должно подождать. Это означает, что поток 1 входит в критическую секцию и начинает делать то, что ему нужно. Затем появляется поток 2 и говорит: «Я хочу использовать критическую секцию». Ядро сообщает: «Поток 1 использует критическую секцию, которую вы должны дождаться своей очереди». Появляется поток 3, и ему говорят то же самое. Потоки 2 и 3 теперь находятся в состоянии ожидания, ожидая освобождения этой критической секции. Когда поток 1 завершает критическую секцию, потоки 2 и 3 соревнуются, кто первым получит критическую секцию, а когда один получит ее, другой должен продолжить ожидание.

В вашем примере, приведенном выше, будет так много ожидания критических секций, что возможно, что поток 1 может находиться в критической секции, а поток 2 ожидает, и до того, как потоку 2 будет предоставлена ​​возможность войти в критическую секцию, поток 1 имеет сделал петлю и снова вошел в нее. Это означает, что поток 1 может завершить всю свою работу до того, как поток 2 когда-либо получит шанс войти в критическую секцию. Следовательно, поддержание минимального объема работы, выполняемой в критической секции по сравнению с остальной частью цикла / функции, поможет потокам работать одновременно.В вашем примере один поток ВСЕГДА будет ждать другой поток, и, следовательно, простое выполнение этого последовательно может быть быстрее, поскольку у вас нет накладных расходов на потоки ядра.

т.е. чем больше вы избегаете CriticalSections, тем меньше времени теряется для потоков, ожидающих друг друга. Однако они необходимы, поскольку вам НЕОБХОДИМО убедиться, что 2 потока не пытаются одновременно работать с одним и тем же объектом. Некоторые встроенные объекты являются "атомарными" , которые могут помочь вам в этом, но для неатомарных операций критическая секция является обязательной.

Событие - это другой вид объекта синхронизации. По сути, событие - это объект, который может находиться в одном из двух состояний. Включено или нет. Если вы используете WaitForSingleObject для «несигнального» события, то поток будет переведен в спящий режим до тех пор, пока не перейдет в сигнальное состояние.

Это может быть полезно, когда у вас есть поток, который ДОЛЖЕН ждать, пока другой поток что-то завершит. В общем, вы хотите по возможности избегать использования таких объектов синхронизации, поскольку это разрушает параллельность вашего кода.

Лично я использую их, когда у меня есть рабочий поток, ожидающий, когда ему нужно что-то сделать. Поток большую часть времени находится в состоянии ожидания, а затем, когда требуется некоторая фоновая обработка, я сигнализирую о событии. Затем поток оживает и делает то, что ему нужно, перед тем, как вернуться к циклу и снова войти в состояние ожидания. Вы также можете пометить переменную как указывающую, что объект должен выйти. Таким образом, вы можете установить для переменной выхода значение true, а затем сигнализировать ожидающему потоку.Ожидающий поток просыпается и говорит: «Я должен выйти», а затем завершается. Однако имейте в виду, что вам «может потребоваться» барьер памяти , который говорит, что убедитесь, что переменная выхода установлена ​​до того, как событие будет разбужено, иначе компилятор может изменить порядок операций.Это может привести к тому, что ваш поток проснется, обнаружив, что переменная выхода не настроена для выполнения своей задачи, а затем вернется в спящий режим. Однако поток, который первоначально послал сигнал, теперь предполагает, что поток завершился, хотя на самом деле это не так.

Кто сказал, что многопоточность - это просто, а? ;)

0
ответ дан 3 September 2019 в 00:46
поделиться

Какая функция, по вашему мнению, должна быть "потокобезопасной"?

Я думаю, что этот термин следует применить к вашей CommonFunction. Это функция, которая, по вашему замыслу, будет вызываться несколькими (в первом случае двумя) потоками.

Я думаю, что в вашем коде есть правило типа:

Thread 2 do some work

meanwhile Thread 3 wait until Thread 2 finishes then you do some work

На самом деле в вашем коде есть

WaitForSingleObject(pthis->thread3->m_hThread,INFINITE);

может быть, ожидает не тот поток?

Но вернемся к безопасности потоков. Где находится контроль безопасности? В логике управления вашими потоками. Предположим, у вас много потоков, как бы вы расширили то, что написали? У вас будет много логики типа:

if thread a has finished and thread b has finished ...

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

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

В последнем случае единственный вопрос - является ли доступ к myArray и myShiftArray потокобезопасными коллекциями

    temp = myArray.GetAt(i);
    myShiftArray.Add(temp);

все остальные переменные локальны, находятся в стеке и принадлежат текущим потокам - так что вам просто нужно обратиться к документации по этим коллекциям, чтобы определить, могут ли они безопасно вызываться отдельными потоками.

2
ответ дан 3 September 2019 в 00:46
поделиться

Похоже, это сработает, потому что потоки не будут выполнять какую-либо работу одновременно. Если вы измените код, чтобы потоки выполняли работу одновременно, вам нужно будет поместить мьютексы (в MFC вы можете использовать для этого CCriticalSection ) вокруг кода, который обращается к элементам данных, которые используются совместно потоки.

0
ответ дан 3 September 2019 в 00:46
поделиться
Другие вопросы по тегам:

Похожие вопросы: