1.题目
3个线程a,b,c。分别打印A , B , C 。用这3个线程循环打印ABC10次,即输出ABCABCABC...
2.分析
1)如何创建子线程
CreateThread是Windows API函数,该函数在主线程的基础上创建一个新线程。
CreateThread创建一个新的线程的大致步骤如下:
a. 在内核对象中分配一个线程标识/句柄,可供管理,由CreateThread返回
b.把线程退出码置为STILL_ACTIVE,把线程挂起计数设置为1
c.分配context结构
d.IpStartAddr和IpvThread值被放在栈顶,使它们成为传送给StartOfThread的参数
e.把context结构的栈指针指向栈顶(d)指令指针指向startOfThread函数
用法:
HANDLE hThread = CreateThread(&security_attributes, dwStackSize, ThreadProc,pParam, dwFlags, &idThread) ;
参数说明:
第一个参数表示线程内核对象的安全属性,一般传入NULL表示使用默认设置。
第二个参数表示线程栈空间大小。传入0表示使用默认大小(1MB)。
第三个参数表示新线程所执行的线程函数地址,多个线程可以使用同一个函数地址。
第四个参数是传给线程函数的参数。
第五个参数指定额外的标志来控制线程的创建,为0表示线程创建之后立即就可以进行调度,如果为CREATE_SUSPENDED则表示线程创建后暂停运行,这样它就无法调度,直到调用ResumeThread()。
第六个参数将返回线程的ID号,传入NULL表示不需要返回该线程ID号。
函数返回值:
成功返回新线程的句柄,失败返回NULL。
2)现在已经生成了3个线程,如何让这3个线程循环打印呢?要实现循环打印就是要实现线程之间的同步a->b->c->a->b->c....
CreateEvent()常用于线程之间的同步。
用法:
hEvent = CreateEvent(NULL,TRUE,TRUE,NULL);
参数说明:
第一个参数表示确定返回的句柄是否可被子进程继承。如果lpEventAttributes是NULL,此句柄不能被继承。
第二个参数表示指定将事件对象创建成手动复原还是自动复原。如果是TRUE,那么必须用ResetEvent函数来手工将事件的状态复原到无信号状态。如果设置为FALSE,当一个等待线程被释放以后,系统将会自动将事件状态复原为无信号状态。
第三个参数表示指定事件对象的初始状态。如果为TRUE,初始状态为有信号状态;否则为无信号状态。
第四个参数表示指定事件的对象的名称,是一个以0结束的字符串指针 。
HANDLE hThread = CreateThread(&security_attributes, dwStackSize, ThreadProc,pParam, dwFlags, &idThread) ;
参数说明:
第一个参数表示线程内核对象的安全属性,一般传入NULL表示使用默认设置。
第二个参数表示线程栈空间大小。传入0表示使用默认大小(1MB)。
第三个参数表示新线程所执行的线程函数地址,多个线程可以使用同一个函数地址。
第四个参数是传给线程函数的参数。
第五个参数指定额外的标志来控制线程的创建,为0表示线程创建之后立即就可以进行调度,如果为CREATE_SUSPENDED则表示线程创建后暂停运行,这样它就无法调度,直到调用ResumeThread()。
第六个参数将返回线程的ID号,传入NULL表示不需要返回该线程ID号。
函数返回值:
成功返回新线程的句柄,失败返回NULL。
2)现在已经生成了3个线程,如何让这3个线程循环打印呢?要实现循环打印就是要实现线程之间的同步a->b->c->a->b->c....
CreateEvent()常用于线程之间的同步。
用法:
hEvent = CreateEvent(NULL,TRUE,TRUE,NULL);
参数说明:
第一个参数表示确定返回的句柄是否可被子进程继承。如果lpEventAttributes是NULL,此句柄不能被继承。
第二个参数表示指定将事件对象创建成手动复原还是自动复原。如果是TRUE,那么必须用ResetEvent函数来手工将事件的状态复原到无信号状态。如果设置为FALSE,当一个等待线程被释放以后,系统将会自动将事件状态复原为无信号状态。
第三个参数表示指定事件对象的初始状态。如果为TRUE,初始状态为有信号状态;否则为无信号状态。
第四个参数表示指定事件的对象的名称,是一个以0结束的字符串指针 。
函数返回值:
如果函数调用成功,函数返回事件对象的句柄。
3)如何使用事件对象.
为了使用事件对象,常用的3个函数为:SetEvent、ResetEvent和WaitForSingleObject。
BOOL SetEvent(HANDLE hEvent); //将事件对象置为有信号
BOOL ResetEvent(HANDLE hEvent); //将事件对象置为无信号
DWORD WaitForSingleObject(HANDLE hHandle,DWORD dwMilliseconds);//等待一个事件有信号或者定时时间到。第一个参数为事件对象句柄,第二个参数是定时时间
4)整体思路:使用全局数组,放入3个事件对象,开启3个线程,每个线程等待相对应的事件,打印完成之后将后面的事件置为有信号。
在主线程中使用
WaitForMultipleObjects(3, hThread, TRUE, INFINITE)等待所有线程完成,之后,退出主线程。
3.代码
HANDLE g_ThreadEvent[3];//事件对象数组 int g_EventIndex = 0; DWORD WINAPI ThreadFunc(void *p) { int param=(int)p; char c='A'+param; for(int iIndex =0; iIndex < 10; iIndex ++) { //线程ABC分别等待事件0,1,2 WaitForSingleObject(g_ThreadEvent[param], INFINITE);//等待信号 printf("the thread is %c\n",c); ResetEvent(g_ThreadEvent[g_EventIndex]);//将对应的信号置为无 g_EventIndex = (g_EventIndex+1)%3; SetEvent(g_ThreadEvent[g_EventIndex]);//将下一个信号置为有信号 } return 0; } int main(int argc, char* argv[]) { //全为自动模式一次只能进去一个 for(int iIndex = 0; iIndex < 3; iIndex++) { g_ThreadEvent[iIndex] = CreateEvent(NULL, false, false, NULL); } SetEvent(g_ThreadEvent[0]); HANDLE hThread[3]; for(int iIndex = 0; iIndex < 3; iIndex++) { hThread[iIndex] = (HANDLE)CreateThread(NULL, 0,ThreadFunc, (void *)iIndex, 0, NULL); } //等待所有线程结束 WaitForMultipleObjects(3, hThread, TRUE, INFINITE); cout<<"运行结束,按任意键退出.....\n"; char c= getchar(); CloseHandle(g_ThreadEvent); return 0; }