Windows多线程程序设计- -

本文介绍了Windows环境下多线程程序的设计与实现,包括线程创建、管理、同步及线程间通信等关键技术。通过实例演示了如何使用CreateThread、WaitForSingleObject等API,并对比了CriticalSection、Mutex、Semaphore等同步机制的特点。

Windows多线程程序设计- -

                                       

1、产生一个线程,只是个框架,没有具体实现。理解::CreateThread函数用法。

#include 

DWORD WINAPI ThreadFunc(LPVOID);

int main()

{

 HANDLE hThread;

 DWORD  dwThreadID;

 hThread = ::CreateThread(NULL,

       0,

       (LPTHREAD_START_ROUTINE)(ThreadFunc),

       NULL,

       0,

       &dwThreadID);

 ...;

 return 0;

}

DWORD WINAPI ThreadFunc(LPVOID lParam)

{

 ...;

 return 0;

}

2、一个真正运转的多线程程序,当你运行它的时候,你会发现(也可能会害怕),自己试试吧。说明了多线程程序是无法预测其行为的,每次运行都会有不同的结果。

#include 

#include 

using namespace std;

DWORD WINAPI ThreadFunc(LPVOID);

int main()

{

 HANDLE hThread;

 DWORD  dwThreadID;

 // 产生5个线程

 for(int i=0; i<5; i++)

 {

  hThread = ::CreateThread(NULL,

        0,

        (LPTHREAD_START_ROUTINE)(ThreadFunc),

        (LPVOID)&i,

        0,

        &dwThreadID);

  if(dwThreadID)

   cout << "Thread launched: " << i << endl;

 }

 // 必须等待线程结束,以后我们用更好的处理方法

 Sleep(5000);

 return 0;

}

DWORD WINAPI ThreadFunc(LPVOID lParam)

{

 int n = (int)lParam;

 for(int i=0; i<3; i++)

 {

  cout << n <<","<< n <<","<< n << ","< }

 return 0;

}

3、使用CloseHandle函数来结束线程,应该是“来结束核心对象的”,详细要参见windows多线程程序设计一书。

修改上面的程序,我们只简单的修改if语句。

 if(dwThreadID)

{

        cout << "Thread launched: " << i << endl;

        CloseHandle(dwThreadID);

}

4GetExitCodeThread函数的用法和用途,它传回的是线程函数的返回值,所以不能用GetExitCodeThread的返回值来判断线程是否结束。

#include 

#include 

using namespace std;

DWORD WINAPI ThreadFunc(LPVOID);

int main()

{

 HANDLE hThread1;

 HANDLE hThread2;

 DWORD  dwThreadID1;

 DWORD  dwThreadID2;

 DWORD  dwExitCode1 = 0;

 DWORD  dwExitCode2 = 0;

 

 hThread1 = ::CreateThread(NULL,

       0,

       (LPTHREAD_START_ROUTINE)(ThreadFunc),

       (LPVOID)1,

       0,

       &dwThreadID1);

 if(dwThreadID1)

  cout << "Thread launched: " << dwThreadID1 << endl;

 hThread2 = ::CreateThread(NULL,

       0,

       (LPTHREAD_START_ROUTINE)(ThreadFunc),

       (LPVOID)2,

       0,

       &dwThreadID2);

 if(dwThreadID2)

  cout << "Thread launched: " << dwThreadID2 << endl;

 

 while(1)

 {

  cout<<"Press any key.";

  cin.get();

  GetExitCodeThread(hThread1, &dwExitCode1);

  GetExitCodeThread(hThread2, &dwExitCode2);

  if( dwExitCode1 == STILL_ACTIVE )

   cout<  if( dwExitCode2 == STILL_ACTIVE )

   cout<        if( dwExitCode1 != STILL_ACTIVE && dwExitCode2 != STILL_ACTIVE )

   break;

 }

 CloseHandle(hThread1);

 CloseHandle(hThread2);

 cout<<"thread 1 returned: "< cout<<"thread 2 returned: "< return 0;

}

DWORD WINAPI ThreadFunc(LPVOID lParam)

{

 DWORD n = (DWORD)lParam;

 Sleep(n*2000);

 return n*10;

}

所以,最终判断线程是否结束还是运行,运用下面的方法,这段代码很重要哦,但它始终是个busy loop,还不是最好的方法。

while(1)

{

 BOOL rc;

 rc = GetExitCodeThread(hThread, dwThreadID);

 if(rc && dwThreadId != STILL_ACTIVE)

  break;

}

5、上面我们已经提到了等待一个线程结束的问题,这里我们讲继续讲述最好的方法。使用WaitForSingleObject(HANDLE, DWORD);

同时上面的busy loop我们可以用这个函数代理了,

WaitForSingleObject(hThread, INFINISH);  看代码吧。

#include 

#include 

using namespace std;

const int NUM_TASKS = 6;

const int THREAD_POOL_SIZE = 3;

const int MAX_THREAD_INDEX = 2;

DWORD WINAPI ThreadFunc(LPVOID);

int main()

{

 HANDLE hThread[THREAD_POOL_SIZE];

 int    slot = 0;

 DWORD  dwThreadID;

 DWORD  dwExitCode = 0;

 

 for(int i=1; i {

  if(i>THREAD_POOL_SIZE)

  {

   WaitForSingleObject(hThread[slot], INFINITE);

   GetExitCodeThread(hThread[slot], &dwExitCode);

   cout<<"Slot "<   CloseHandle(hThread[slot]);

  }

  hThread[slot] = CreateThread(NULL,

         0,

         (LPTHREAD_START_ROUTINE)ThreadFunc,

         (LPVOID)slot,

         0,

         &dwThreadID);

  cout<<"launched thread "<  if(++slot>MAX_THREAD_INDEX)

   slot=0;

 }

 

 for(slot=0; slot {

  WaitForSingleObject(hThread[slot], INFINITE);

  CloseHandle(hThread[slot]);

 }

 

 cout<<"all thread terminated."< return 0;

}

DWORD WINAPI ThreadFunc(LPVOID lParam)

{

 srand(GetTickCount());

 Sleep((rand()%8)*500+500);

 cout<<"slot "<<(DWORD)lParam<<" idle."< return (DWORD)lParam;

}

我们发现,调用WaitForSingleObject()并放置一个“线程核心对象”作为参数,将是调用线程#1开始睡眠,直到线程#2(我们刚刚说的线程核心对象)结束为止。就想Sleep()函数一样。INFINITE代表无穷等待,呵呵。

6、使用WaitForMultipleObject(DWORD nCount, CONST HANDLE* lpHandles, BOOL bWaitAll, DWORD dwMilliSeconds)

解释一下参数:

nCount指的是lpHandles数组元素的个数。

lpHandles指的是核心对象数组。

bWaitAll一般为TRUE

dwMilliSeconds一般为INFINITE

 7、下面我们要接触到的是同步问题了,如果你不知道同步是什么,最好上网搜索一下。

简单的一个含有Critical_Section的链表的代码:

typedef struct _Node

{

 struct _Node* next;

 int data;

}Node;

typedef struct _List

{

 Node* head;

 Node* tail;

 CRITICAL_SECTION critical_sec;

}List;

List* CreateList()

{

 List *pList = new List;

 pList->head = NULL;

 pList->tail = NULL;

 InitializeCriticalSection(&pList->critical_sec);

 return pList;

}

DeleteCriticalSection(&pList->critical_sec);

 do

 {

  Node* node;

  node = pList->head;

  delete node;

 }while(pList->head = pList->head->next != NULL)

void AddHead(List* pList, Node* newNode)

{

 EnterCriticalSection(&pList->critical_sec);

 newNode->next = pList->head;

 pList->head = newNode;

 LeaveCriticalSection(&pList->critical_sec);

}

void AddTail(List* pList, Node* newNode)

{

 EnterCriticalSection(&pList->critical_sec);

 pList->tail->next = newNode;

 newNode->next = NULL;

 pList->tail = newNode;

 LeaveCriticalSection(&pList->critical_sec);

}

Node* Next(List* pList, Node* node)

{

 Node* Next;

 EnterCriticalSection(&pList->critical_sec);

 Next = node->next;

 LeaveCriticalSection(&pList->critical_sec);

 return next;

}

不知道有没有问题,自己没有测试,如果你有心就测试一下吧。

另外还有Mutex

Mutex

CreateMutex()

OpemMutex()

WaitForSingleObject()

WaitForMultipleObjects()

MsgWaitForMultipleObjects()

ReleaseMutex()

CloseHandle()

下面是一个交换链表的Mutex操作。

struct Node

{

 struct Node* next;

 int data;

};

struct List

{

 struct Node* head;

 HANDLE hMutex;

};

void SwapLists(List* list1, List* list2)

{

 List* tmp_list;

 HANDLE arrHandle[2];

 arrHandle[0] = list1->hMutex;

 arrHandle[1] = list2->hMutex;

    WaitForMultipleObjects(2, arrHandle, TRUE, INFINITE);

 tmp_list = list1->head;

 list1->head = list2->head;

 list2->head = tmp_list;

 ReleaseMutex(arrHandle[0]);

 ReleaseMutex(arrHandle[1]);

}

Critical_Section不同,Mutex可以跨进程使用,以及跨线程使用。Mutex可以根据名称被开启。所以,另一个进程可以完全不需要和产生Mutex的进程打招呼,就根据名称开启一个Mutex。这里要注意CreateMutex第二个参数。

另外还有SemphoreEventInterlockIncrementInterlockDecrement,其中Event有很大的灵活性,这里不举例了,感兴趣就上网搜索一下吧。

下面我们对比一下上面的几个同步对象。

Critical Section

用来实现“排他占有”。适用范围是单一进程的各个线程之间。它是:

1、一个局部性对象,不是一个核心对象。

2、快速而有效率。

3、不能够同时有一个以上的Critical_Section被等待。

4、无法侦测是否已被某个线程放弃。

Mutex

一个核心对象,可以在不同的线程之间实现“排他性占有”,甚至即使那些线程分属不同进程。它是:

1、一个核心对象。

2、如果拥有mutex的那个线程结束,则会产生一个“abandoned”错误信息。

3、可是使用Wait...()等待一个mutext

4、可以具名,因此可以被其他进程开启。

5、只能被拥有它的那个线程释放(released)。

Semaphore 

被用来追踪有限资源。它是:

1、一个核心对象。

2、没有拥有者。

3、可以具名,因此可以被其他进程开启。

4、可以被任何一个线程释放(released)

Event

通常用于overlapped I/O,或用来设计某些自定义的同步对象。它是:

1、一个核心对象。

2、完全在程序掌控之下。

3、适用于设计新的同步对象。

4、“要求苏醒”的请求并不会被存储起来,肯能会被遗失掉。

5、可以具名,因此可以被其他进程开启。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值