Консультация № 173619
23.10.2009, 23:22
0.00 руб.
0 2 1
Здравствуйте!Помогите,пожалуйста,разобраться со следующим вопросом:
Есть задача по синхронизации потоков.
Есть некоторое число потоков(>3).Нужно,чтобы каждый поток запускался и выводил на консоль сообщение ,что он запустился,потом этот поток спит несколько секунд (с помощью функции Sleep) и выводит на консоль сообщение, что он завершился.Сообщения должны выводиться в хаотичном порядке.Например:
поток 1 запустился
поток 3 запустился
поток 1 завершился
поток 2 запустился и т.д. в цикле.
Синхронизацию осуществляю с помощью CRITICAL SECTION .Проблема именно в хаотичности.(os windows xp,среда builder c++6)
Помогите новичку!пробная версия в приложении!

Приложение:
//---------------------------------------------------------------------------

#pragma hdrstop
#include<windows.h>
#include<iostream.h>
#include<conio.h>
#define WIN32_LEAN_AND_MEAN
#define THREAD_COUNT 4
CRITICAL_SECTION cs;
DWORD WINAPI Thread_1(LPVOID p)
{
EnterCriticalSection(&cs);
cout<<"thread#1 started"<<GetCurrentThreadId()<<endl;
Sleep(500);
cout<<"thread#1 finished"<<GetCurrentThread()<<endl;
LeaveCriticalSection(&cs);
return 0;
}
DWORD WINAPI Thread_2(LPVOID p)
{
EnterCriticalSection(&cs);
cout << "thread #2 started: " << GetCurrentThreadId() << endl;
Sleep(4000);
cout << "thread #2 finished: " << GetCurrentThreadId() << endl;
LeaveCriticalSection(&cs);

return 0;
}
DWORD WINAPI Thread_3(LPVOID p)
{
EnterCriticalSection(&cs);
cout << "thread #3 started: " << GetCurrentThreadId() << endl;
Sleep(4000);
cout << "thread #3 finished: " << GetCurrentThreadId() << endl;
LeaveCriticalSection(&cs);

return 0;
}
DWORD WINAPI Thread_4(LPVOID p)
{
EnterCriticalSection(&cs);
cout << "thread #4 started: " << GetCurrentThreadId() << endl;
Sleep(3000);
cout << "thread #4 finished: " << GetCurrentThreadId() << endl;
LeaveCriticalSection(&cs);

return 0;
}

//---------------------------------------------------------------------------

#pragma argsused
int main(int argc, char* argv[])
{


HANDLE hThread_1, hThread_2,hThread_3,hThread_4;
DWORD IDThread_1, IDThread_2,IDThread_3,IDThread_4;
/*HANDLE* phThreads=new HANDLE[THREAD_COUNT];
if(!phThreads) return -1; */
InitializeCriticalSection(&cs);
for(int iCounter=0;iCounter<THREAD_COUNT;)
{
++iCounter;




// запускаем поток 1
hThread_1 = CreateThread(NULL, 0, Thread_1, (PVOID)iCounter, 0, &IDThread_1);
if (hThread_1 == NULL)
return GetLastError();

// запускаем поток 2
hThread_2 = CreateThread(NULL, 0, Thread_2, (PVOID)iCounter, 0, &IDThread_2);
if (hThread_2 == NULL)
return GetLastError();
// запускаем поток 3
hThread_3 = CreateThread(NULL, 0, Thread_3, (PVOID)iCounter, 0, &IDThread_3);
if (hThread_3 == NULL)
return GetLastError();
// запускаем поток 4
hThread_4 = CreateThread(NULL, 0, Thread_4, (PVOID)iCounter, 0, &IDThread_4);
if (hThread_4 == NULL)
return GetLastError();
}


// ждем, пока поток Thread_1 закончит работу
WaitForSingleObject(hThread_1, INFINITE);
// ждем, пока поток Thread_2 закончит работу
WaitForSingleObject(hThread_2, INFINITE);
// ждем, пока поток Thread_3 закончит работу
WaitForSingleObject(hThread_3, INFINITE);
// ждем, пока поток Thread_4 закончит работу
WaitForSingleObject(hThread_4, INFINITE);


// закрываем дескриптор потока Thread_1
CloseHandle(hThread_1);
// закрываем дескриптор потока Thread_2
CloseHandle(hThread_2);
// закрываем дескриптор потока Thread_3
CloseHandle(hThread_3);
// закрываем дескриптор потока Thread_4
CloseHandle(hThread_4);
DeleteCriticalSection(&cs);
getch();

return 0;
}
//----------------------------------------

Обсуждение

Неизвестный
24.10.2009, 00:31
общий
это ответ
Здравствуйте, РАИ.
Как я вижу, Вы переделали мою программу. Парочка замечаний.

- если несколько потоков должны выполнять одинаковую работу, совершенно незачем для каждого из них делать отдельную функцию. Можно обойтись одной функцией, передавая ей разные параметры. Сохраняйте нужные данные локально (в стеке) или выделяйте память для потока (Thread Local Storage). См. описания (TlsAlloc,TlsGetValue,TlsSetValue,TlsFree). В данном случае достаточно сохранить номер потока в локальной переменной. Написание отдельных одинаковых функций для каждого потока свидетельствует о непонимании того, как работают потоки.

- в вашем варианте есть серьезная (с моей точки зрения) ошибка. Вы пишете:

EnterCriticalSection(&cs);
cout<<"thread#1 started"<<GetCurrentThreadId()<<endl;
Sleep(500);
cout<<"thread#1 finished"<<GetCurrentThread()<<endl;
LeaveCriticalSection(&cs);

При этом выполнение очередного потока начнется только после завершения предыдущего.
Нужно синхронизировать только вывод на консоль:

EnterCriticalSection(&cs);
cout<<"thread#1 started"<<GetCurrentThreadId()<<endl;
LeaveCriticalSection(&cs);

Sleep(500);

EnterCriticalSection(&cs);
cout<<"thread#1 finished"<<GetCurrentThread()<<endl;
LeaveCriticalSection(&cs);

- директива #define WIN32_LEAN_AND_MEAN должна быть ПЕРЕД #include<windows.h>, иначе она не имеет смысла.

Переработанный вариант программы — в приложении. Директивы #pragma, специфические для Borland C++ Builder, я закомментировал. Проверял с MSVC 6.0.

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

thread 4 started 9604
thread 1 started 8396
thread 3 started 8816
thread 2 started 8300
thread 3 finished 8816
thread 1 finished 8396
thread 4 finished 9604
thread 2 finished 8300

All threads finished. Press any key to exit.

Успехов!

P.S. Если будут вопросы, то ответить смогу только в понедельник после 13-14 часов.

Приложение:
//#pragma hdrstop
#define WIN32_LEAN_AND_MEAN
#include<windows.h>
#include<iostream.h>
#include<conio.h>
#include<stdlib.h>

#define THREAD_COUNT 4

CRITICAL_SECTION cs;

int iThread[THREAD_COUNT]; // индексы потоков, по стандарту инициализируется нулями

DWORD WINAPI ThreadFunc(LPVOID p)
{
int iNum = *(int*)p;
EnterCriticalSection(&cs);
cout<<"thread " << iNum << " started " << GetCurrentThreadId() << endl;
LeaveCriticalSection(&cs);

// wait random interval
Sleep( 500 + rand()*5000/RAND_MAX );

EnterCriticalSection(&cs);
cout<<"thread " << iNum << " finished " << GetCurrentThreadId() << endl;
LeaveCriticalSection(&cs);
return 0;
}

//---------------------------------------------------------------------------

//#pragma argsused
int main( int argc, char* argv[] )
{
HANDLE* phThreads = new HANDLE[THREAD_COUNT];
if(!phThreads) return -1;

// initialize the the random-number generator with current time so that
// the numbers will be different every time we run
srand( (unsigned)GetTickCount() );


InitializeCriticalSection(&cs);
for( int i = 0; i < THREAD_COUNT; ++i )
{
int n;
do {
// rand() returns a pseudorandom integer in the range 0 to RAND_MAX
n = ( rand()*THREAD_COUNT )/RAND_MAX;
if( n == THREAD_COUNT ) --n; // редкий случай, когда rand() возвращает RAND_MAX
} while( iThread[n] );
iThread[n] = n+1;

DWORD dwThreadId;
phThreads[n] = CreateThread( NULL, 0, ThreadFunc, (PVOID)(iThread+n), 0, &dwThreadId );
if (phThreads[n] == NULL)
return GetLastError();
}
// подождем, пока все потоки завершатся
WaitForMultipleObjects( THREAD_COUNT, phThreads, TRUE, THREAD_COUNT*5000 );

// критическая секция больше не нужна, освобождаем ресурс
DeleteCriticalSection( &cs );

// закрываем описатели потоков
while( i > 0 )
CloseHandle( phThreads[--i] );

// освобождаем память
delete[] phThreads;

cout << endl << "All threads finished. Press any key to exit." << endl;
getch();
return 0;
}
Неизвестный
28.10.2009, 14:15
общий
Могу добавить, что приведение псевдослучайного числа, выдаваемого функцией rand() к нужному диапазону можно выполнить и по-другому, например:

n = rand() % THREAD_COUNT;
Форма ответа