操作系统为每一个运行线程安排一定的CPU时间 —— 时间片。系统通过一种循环的方式为线程提供时间片,线程在自己的时间内运行,多个线程不断地切换运行,因时间片相当短,因此,给用户的感觉,就好像线程是同时运行的一样。
单cpu计算机一个时间只能运行一个线程,如果计算机拥有多个CPU,线程就能真正意义上同时运行了。
windows平台下,创建线程可以使用windows api 函数CreateThread来实现,函数声明是:
WINBASEAPI
HANDLE
WINAPI
CreateThread( LPSECURITY_ATTRIBUTES lpThreadAttributes, DWORD dwStackSize, LPTHREAD_START_ROUTINE lpStartAddress, LPVOID lpParameter, DWORD dwCreationFlags, LPDWORD lpThreadId );
参数说明:
lpThreadAttributes | 线程安全性,使用缺省安全性,一般缺省null |
dwStackSize | 堆栈大小,0为缺省大小 |
lpStartAddress | 线程要执行的函数指针,即入口函数 |
lpParameter | 线程参数 |
dwCreationFlags | 线程标记,如为0,则创建后立即运行 |
lpThreadId | LPDWORD为返回值类型,一般传递地址去接收线程的标识符,一般设为null |
#include <windows.h>
另外需要标准输入输出函数,所以包含:
#include <iostream.h>
以多个售票窗口卖同一张火车票为例,定义一个全局的票数tickets,用两个线程来执行卖票,两个线程访问同一个变量tickets,先看一个不正确的写法:
//问题程序
#include <windows.h>
#include <iostream.h>
DWORD WINAPI Fun1Proc(
LPVOID lpParameter
);
DWORD WINAPI Fun2Proc(
LPVOID lpParameter
);
int tickets=100;
void main()
{
HANDLE hThread1;
HANDLE hThread2;
hThread1=CreateThread(NULL,0,Fun1Proc,NULL,0,NULL);
hThread2=CreateThread(NULL,0,Fun2Proc,NULL,0,NULL);
CloseHandle(hThread1);
CloseHandle(hThread2);
system("pause");
}
DWORD WINAPI Fun1Proc(
LPVOID lpParameter
)
{
while(TRUE)
{
if(tickets>0)
{
Sleep(1);//假定为卖票需要花费的时间
cout<<"thread1 sell ticket : "<<tickets--<<endl;
}
else
break;
}
return 0;
}
DWORD WINAPI Fun2Proc(
LPVOID lpParameter // thread data
)
{
while(TRUE)
{
if(tickets>0)
{
Sleep(1);
cout<<"thread2 sell ticket : "<<tickets--<<endl;
}
else
break;
}
return 0;
}
线程中sleep(1);表名该线程放弃执行的权利,操作系统会选择另外的线程进行执行。所以执行结果是: ![Alt test]
WINBASEAPI
HANDLE
WINAPI
CreateEvent(
LPSECURITY_ATTRIBUTES lpEventAttributes,
BOOL bManualReset,
BOOL bInitialState,
LPCWSTR lpName
);
参数说明:
lpEventAttributes | 安全性,采用null默认安全性。 |
bManualReset | (TRUE)人工重置或(FALSE)自动重置事件对象为非信号状态,若设为人工重置,则当事件为有信号状态时,所有等待的线程都变为可调度线程。 |
bInitialState | 指定事件对象的初始化状态,TRUE:初始为有信号状态。 |
lpName | 事件对象的名字,一般null匿名即可。 |
#include <windows.h>
#include <iostream.h>
DWORD WINAPI Fun1Proc(LPVOID lpParameter );
DWORD WINAPI Fun2Proc(LPVOID lpParameter );
int tickets=100;
HANDLE g_hEvent;
void main()
{
HANDLE hThread1;
HANDLE hThread2;
hThread1=CreateThread(NULL,0,Fun1Proc,NULL,0,NULL);
hThread2=CreateThread(NULL,0,Fun2Proc,NULL,0,NULL);
CloseHandle(hThread1);
CloseHandle(hThread2);
g_hEvent=CreateEvent(NULL,TRUE,FALSE,"tickets");
system("pause");
CloseHandle(g_hEvent);
}
DWORD WINAPI Fun1Proc(LPVOID lpParameter )
{
while(TRUE)
{
WaitForSingleObject(g_hEvent,INFINITE);
ResetEvent(g_hEvent);
if(tickets>0)
{
Sleep(1);
cout<<"thread1 sell ticket : "<<tickets--<<endl;
}
else
break;
SetEvent(g_hEvent);
}
return 0;
}
DWORD WINAPI Fun2Proc(LPVOID lpParameter)
{
while(TRUE)
{
WaitForSingleObject(g_hEvent,INFINITE);
ResetEvent(g_hEvent);
if(tickets>0)
{
Sleep(1);
cout<<"thread2 sell ticket : "<<tickets--<<endl;
}
else
break;
SetEvent(g_hEvent);
}
return 0;
}
初始化中将时间设为手动重置:
g_hEvent=CreateEvent(NULL,TRUE,FALSE,"tickets");
具体思路是:其中一个线程等待到信号之后,重置事件,进行数据处理,处理完成之后再次触发事件。另一个线程收到新的信号,同样处理并重置事件。先来看看执行结果:
g_hEvent=CreateEvent(NULL,FALSE,FALSE,NULL);
SetEvent(g_hEvent);
线程中请求到该事件信号,自动将事件置为无信号状态,线程按cpu分配的时间片去执行,等到执行完毕,将事件信号触发为有信号状态,另一个线程此时请求到该信号,进行同样的执行。线程while循环为:
while(TRUE)
{
WaitForSingleObject(g_hEvent,INFINITE);//收到信号,自动重置
if(tickets>0)
{
Sleep(1);
cout<<"thread1 sell ticket : "<<tickets--<<endl;
}
else
break;
SetEvent(g_hEvent);
}
与mutex对象.不同的是,设为人工重置的事件对象,如果重复SetEvent()设为有信号状态,若想重置只需要执行一次ResetEvent()即可。
g_hEvent=CreateEvent(NULL,TRUE,FALSE,"tickets");
if(g_hEvent)
{
if(ERROR_ALREADY_EXISTS==GetLastError())
{
cout<<"only instance can run!"<<endl;
return;
}
}
命名事件对象的一种应用是:通过命名事件对象,可以保证当前只有一个应用程序实例在运行。
以上是关于windows平台下多线程同步相关的事件对象的使用问题,之后将对线程同步的关键代码段进行介绍和解析,敬请关注。文中如有谬误,还望不吝赐教。
版权声明:本文为csdn博主[applebite](http://blog.csdn.net/applebite) 原创文章,转载请注明出处,侵权必究。 微信添加公众号:qi_chuang 或 猎奇创新平台