网络编程——多线程与异步套接字编程(API) 学习

1.多线程技术

1.1 创建线程

在VC中编写程雪,调用API函数CreateThread()创建线程。

该函数原型如下:

HANDLE CreateThread(
LPSECURITY_ATTRIBUTES lpThreadAttributes,
DWORD dwStackSize,
LPTHREAD_START_ROUTINE lpStartAddress,
DWORD dwCreationFlags,
LPDWORD lpThreadId
);
该函数的作用是用于创建一个线程,并将返回该线程的句柄。其中,各个参数含义如下:
参数lpThreadAttributes是一个指向结构体SECURITY_ATTRIBUTES的指针,表示指定新建线程的安全属性。该参数可以设置为NULL,表示创建线程时使用默认的安全属性。
参数dwStackSize指定线程初始化时地址空间的大小。如果这个参数指定为0,那么新创建线程的地址空间大小与调用该函数的线程地址空间大小一样。
参数lpStartAddress将指定该线程的线程函数的地址。当线程创建成功以后,新建线程将调用该线程函数执行某个功能。
参数lpParameter表示将要传递给新建线程的命令行参数。新建线程可以根据该命令参数的不同而执行不同的功能。
参数dwCreationFlags用于指定新线程创建后是否立即运行。                                                 

dwCreationFlags取值 作用
CREATE_SUSPENDED 线程创建成功后暂停运行
0
线程创建成功后立即运行

注意:当用户创建线程时,将该参数值指定为CREATE_SUSPENDED,则线程将处于暂停状态,直到用户调用相关函数将线程恢复运行为止。
参数lpThreadId表示新建线程的ID号。在这里,用户可以将该参数设置为NULL。

2 实现线程同步

2.1 临界区对象

使用API函数操作临界区步骤

(1)定义临界区对象

        CRITICAL_SECTION m_sec;

(2)初始化临界区对象

        InitialzeCritivcalSection(&m_sec);

(3)线程进入临界区并拥有临界区对象的所有权

        EnterCriticalSection(&m_sec));

(4)线程释放临界区的所有权并离开临界区

        LeaveCriticalSection(&m_sec);

(5)用户调用DeleteCriticalSection(&m_sec)函数将该临界区从内存中删除


2.2 事件对象

事件对象是指用户在程序中使用内核对象的有无信号状态实现线程的同步。


使用函数CreateEvent()创建事件对象并返回事件对象

HANDLE CreateEvent(
  LPSECURITY_ATTRIBUTES lpEventAttributes,        
  BOOL bManualReset,
  BOOL bInitialState,
  LPCTSTR lpName
);

如果该函数调用成功,则返回新创建的事件对象。其参数及意义如下:
参数lpEventAttributes是结构体SECURITY_ATTRIBUTES的指针,表示新创建的事件对象的安全属性。如果该参数为NULL,则表示程序使用的是默认安全属性。
参数bManualReset表示所创建的事件对象是人工重置还是自动重置。如果该参数为true,则表示程序所创建的事件对象为人工重置对象。如果为false,则表示创建的事件对象为自动重置对象。
参数bInitialState表示事件对象的初始状态。如果该参数为true,则表示该事件对象初始时为有信号状态。否则,表示事件对象初始化时为无信号状态。
参数lpName表示事件对象的名称。如果该参数为NULL,则表示程序创建的是一个匿名的事件对象。

注意:如果参数bManualReset设置为true,则表示当调用线程获得其所有权后,用户需要显式地调用函数ResetEvent()将事件对象设置为无信号状态。如果为自动重置的事件对象,则系统会自动将其设置为无信号状态。所以,一般情况下用户编程均将事件对象设置为自动重置。
例如,用户创建一个初始化状态为有信号并且是自动重置的事件对象。代码如下:

HANDLE hevent;                        
//定义事件对象
Hevent=::CreateEvent(NULL,false,true,NULL);
//创建事件对象
...
//省略部分代码

当用户创建事件对象时,如果将其初始状态设置为无信号,则需要用户手动将其设置为有信号状态。实现该功能可以调用函数SetEvent()将指定的事件对象设置为有信号状态。该函数原型如下:

BOOL SetEvent(HANDLE hEvent);

该函数调用成功,则返回true。否则,将返回false。

当然,线程也可以通过调用函数WaitForSingleObject()主动请求事件对象。该函数原型如下:

DWORD WaitForSingleObject(
  HANDLE hHandle,        
  DWORD dwMilliseconds   
);

该函数将在用户指定的事件对象上等待。如果事件对象处于有信号状态,函数将返回。否则,函数将一直等待,直到用户所指定的时间到达。各参数及其意义如下:
参数hHandle表示函数所等待的事件对象句柄。
参数dwMilliseconds表示该函数将在事件对象上的等待时间,如果该参数为INFINITE,则该函数将永远等待。

2.3 互斥对象

互斥对象与前面所学的临界区对象那个和事件对象的作用一样,均用于实现线程同步。但是,互斥对象还可以在进程之间使用。在互斥对象中,包含一个线程ID和一个计数器。线程ID表示拥有该互斥对象的线程,计数器用于表示该互斥对象被同一线程所拥有的次数。


用户可以调用API函数CreateMutex()创建并返回互斥对象。该函数原型如下:

HANDLE CreateMutex(
  LPSECURITY_ATTRIBUTES lpMutexAttributes,
   BOOL bInitialOwner,  
  LPCTSTR lpName     
);

如果该函数调用成功,将返回新创建的互斥对象句柄。否则,将返回NULL。各参数及其意义如下:
参数lpMutexAttributes指定新创建互斥对象的安全属性。如果该参数为NULL,表示互斥对象拥有默认的安全属性。
参数bInitialOwner表示该互斥对象的拥有者。如果为true,则表示创建该互斥对象的线程拥有其所有权。如果为false,表示创建互斥对象的线程不能拥有该互斥对象的所有权。
参数lpName表示互斥对象的名称。若该参数为NULL,则表示程序创建的是匿名对象。如果用户为该参数指定值,则在程序中可以调用函数OpenMutex()打开一个命名的互斥对象。

例如,用户创建一个匿名的互斥对象,代码如下:

HANDLE hmutex;                    
//声明互斥对象句柄
hmutex=::CreateMutex(NULL,FALSE,NULL):    
//创建互斥对象并返回其句柄
...                                
//省略部分代码

线程使用完该互斥对象以后,用户应该调用函数ReleaseMutex()释放对该互斥对象的所有权,也就是让互斥对象处于有信号状态。函数ReleaseMutex()的原型如下:

BOOL ReleaseMutex(HANDLE hMutex);

如果该函数调用成功,则返回true。否则,将返回false。参数hMutex表示将释放的互斥对象句柄。

例如,用户将上面创建的互斥对象句柄hmutex与调用该句柄的线程进行分离。代码如下:

...                                
//省略部分代码
::ReleaseMutex(hmutex);                    
//释放互斥对象句柄

在互斥对象中,线程也可以调用函数WaitForSingleObject()对该对象进行请求。当互斥对象无信号时,该函数将一直等待,直到该互斥对象有信号或用户所指定的等待时间已过。否则,该函数将返回。


3 进程间通信

3.1 油槽

3.2

你可能感兴趣的:(网络编程——多线程与异步套接字编程(API) 学习)