网络编程——多线程技术
多线程共存于应用程序中是现代操作系统中的基本特征和重要标志。为了提高程序的运行效率,在操作系统中提出了进程和线程的概念,在一个进程中可以包含多个线程,进程作为资源分配的基本单位,线程作为独立运行和独立调度的基本单位。既然提到了进程和线程,就涉及到进程(线程)的并发执行以及互斥对象的访问。这些在网络编程中都是十分重要的知识点。具体操作系统的知识就不做介绍了,下面通过实例详细介绍网络编程中的多线程技术。
在程序运行时,操作系统会创建一个进程,同时就会产生一个主线程,我们可以在主线程中继续创建线程,来完场我们制定的操作。
1、创建线程,要用到创建线程的函数CreateThread(),这个函数的第三个参数制定了新创建线程的线程函数,该线程的所有操作都可以在该线程函数中完成。线程函数可以参看MSDN的详细用法。线程函数的形式如下:DWORD WINAPI ThreadProc(LPVOID lpParameter);每一个线程都有一个线程句柄与之相关联。
2、线程同步:在一个进程中可以创建多个线程,这多个线程在需要访问同一个资源时,肯定会发生争用现象,在争夺资源的过程中,加入第一个线程先访问这一资源,并对其做了修改,在这个线程没有执行完毕但时间片到了,第二个线程又访问该资源,就可能得到错误的结果。这是非常严重的问题。为了解决这一问题,引入了进程同步的概念,我们可以创建互斥对象来解决这一问题。创建互斥对象后,某线程只有得到互斥对象的访问权后,才可以访问临界资源,即使该线程的时间片到,如果该线程不释放互斥对象的使用权,下一个线程由于获得不到互斥对象,导致第二个线程无法访问临界资源。这就防止了线程在争夺资源的过程中发生不可预知的错误。创建互斥对象的函数为:CreateMutex(),该函数返回互斥对象的句柄。
3、获得临界资源的访问权:通过调用函数WaitForSingleObject()可以获得临界资源的访问权,如果得到访问权后,该线程不主动释放该互斥对象,即使该线程的时间片到,下一个线程由于不能得到访问权而不能访问临界资源。
4、释放互斥对象:该线程访问临界资源完毕,应该释放互斥对象,让其他线程有机会获得访问临界资源的权利。释放互斥对象可以调用函数ReleaseMutex()来实现。
注意:
1、互斥对象是和线程相关的,哪个线程需要访问临界资源,必须在该线程内部调用WaitForSingleObject()函数
2、线程执行完毕,应释放互斥对象。必须是“谁拥有谁释放”,必须在线程执行完毕之前释放,不能在下一个线程的开始释放。因为互斥对象是和线程相关的,里面维护有线程句柄来识别时哪个线程调用该函数。
3、“拥有几次释放几次”:在互斥对象内部维护有一个计数器,可以统计获得互斥对象的次数,只有计数器为零时,下一个线程才可以获得该互斥对象。所以调用几次获得函数就需要释放几次。
下面通过一个火车售票系统的简化模型来具体实现多线程技术的应用:
假设共有100张火车票可以卖出,现在有两个线程都可以卖票,为了使卖票系统不会出现差错,就需要用到互斥对象。示例代码如下:
#include "stdafx.h"
#include <Windows.h>
#include <iostream>
using namespace std;
DWORD WINAPI ThreadProc1(
LPVOID lpParameter // thread data
);
DWORD WINAPI ThreadProc2(
LPVOID lpParameter // thread data
);
int tickets=100;
HANDLE hMutex;
int _tmain(int argc, _TCHAR* argv[])
{
HANDLE hThread1;
HANDLE hThread2;
hThread1=CreateThread(NULL,0,ThreadProc1,NULL,0,NULL);//创建线程1
hThread2=CreateThread(NULL,0,ThreadProc2,NULL,0,NULL);//创建线程2
CloseHandle(hThread1);
CloseHandle(hThread2);
hMutex=CreateMutex(NULL,TRUE,NULL);//创建互斥对象
ReleaseMutex(hMutex);//必须在主线程中释放互斥对象
Sleep(1000);//必须让主线程暂停执行,如果主线程退出了,其他线程就不复存在了
system("pause");
return 0;
}
/****************************************************************
线程函数的实现
******************************************************************/
DWORD WINAPI ThreadProc1(LPVOID lpParameter)
{
while (TRUE)
{
WaitForSingleObject(hMutex,INFINITE);
if (tickets>0)
{
Sleep(1);
cout<<"Thread1 sell tickets:"<<tickets--<<endl;
}
else
break;
ReleaseMutex(hMutex);
}
return 0;
}
DWORD WINAPI ThreadProc2(LPVOID lpParameter)
{
while (TRUE)
{
WaitForSingleObject(hMutex,INFINITE);
if (tickets>0)
{
Sleep(1);
cout<<"Thread2 sell tickets:"<<tickets--<<endl;
}
else
break;
ReleaseMutex(hMutex);
}
return 0;
}
运行结果如下:
在上面这个示例中,在主线程中创建互斥对象后,必须释放互斥对象,线程1和线程2才有机会拥有互斥对象的所有权。