首页 诗词 字典 板报 句子 名言 友答 励志 学校 网站地图
当前位置: 首页 > 教程频道 > 操作系统 > windows >

Windows下多线程同步相关的MFC类

2013-01-23 
Windows上多线程同步相关的MFC类转载请标明出处:http://blog.csdn.net/zhangxingpingWindows上多线程同步

Windows上多线程同步相关的MFC类
转载请标明出处:http://blog.csdn.net/zhangxingping

Windows上多线程同步相关的MFC类相关类

MFC中提供的用于多线程程序中进行线程同步的类可以分为两类:同步类和同步访问类。

同步类

synchronization class

用于确保对资源完整性访问进行控制的类

CSyncObject

CSemaphore,

CMutex

CCriticalSection,

CEvent

同步访问类

Synchronization access class

用于获取受控资源访问权限的类

CMultiLock

CSingleLock

 

上面表格中的CSyncObject类为纯虚类,在实际中几乎不会直接使用它。它也是其它同步类的直接父类。

 

那么,在实际中应该如何判断到底需要使用上面的那个同步类了?一般遵循一下的原则:

1.        如果线程对资源的访问必须是在某个事件发生之后,那么可以使用CEvent。

2.        如果同一个程序中的多个线程可以在同一时刻访问资源,那么可以使用CSemaphore。

3.        如果多个程序可以访问资源,则可以使用CMutex;否则,使用CCriticalSection。

程序示例CEvent

CEvent在线程必须等待某个事件发生后才能继续执行的情况下非常有用。它有两种工作模式:手动和自动。对于手动模式来说,事件的状态会一直保持到下一次调用SetEvent 或者 ResetEvent ;对于自动模式来说,事件的状态会在唤醒至少一个线程之后重新恢复到不可用的状态。

 

下面的代码演示了CEvent的典型用法。程序中通过两个线程来分别输出20个a和b字符,但是我们期待这两个字符的输出是相间的,也就是不要出现连续的a或者连续的b。

Visual Studio创建的控制台程序,支持MFC:

//CCriticalSectionDemo.cpp : 定义控制台应用程序的入口点。// #include "stdafx.h"#include "CCriticalSectionDemo.h"#include "afxmt.h" #include <conio.h>#include <windows.h> #ifdef _DEBUG#define new DEBUG_NEW#endif  // 唯一的应用程序对象 CWinApp theApp; using namespacestd; const intsize= 10;const inttotalCount= 10; int data[size] = { 0 }; CCriticalSection cs; //线程A:用来输出数组中的全部数值;输出次UINT __cdecl PrintThread( LPVOIDpParam ){    for ( int count = 0; count < totalCount;count++)    {        cs.Lock();//每次输出之前,先申请访问临界区         for( int index = 0; index < size;index++)        {            cout<< data[index]<< " ";            Sleep(200);        }        cout<< endl;         cs.Unlock();//资源访问完毕后,释放之            }     return 0; } //线程B:用来修改数组中的全部数值UINT __cdecl ModifyThread( LPVOIDpParam ){       for ( int count = 0; count < totalCount;count++)    {        cs.Lock();//每次修改资源之前,申请先         for( int index = 0; index < size;index++)        {             data[index] =count;             Sleep(200);        }               cs.Unlock();//资源使用完毕,释放之    }     return 0;}  int _tmain(intargc,TCHAR*argv[],TCHAR*envp[]){    int nRetCode = 0;     // 初始化MFC并在失败时显示错误    if (!AfxWinInit(::GetModuleHandle(NULL),NULL, ::GetCommandLine(), 0))    {        // TODO: 更改错误代码以符合您的需要        _tprintf(_T("错误: MFC初始化失败\n"));        nRetCode= 1;    }    else    {        // TODO: 在此处为应用程序的行为编写代码。         CWinThread* pPrintThread = AfxBeginThread(PrintThread,NULL);        CWinThread* pModifyThread = AfxBeginThread(ModifyThread,NULL);         WaitForSingleObject(pPrintThread->m_hThread,INFINITE);        WaitForSingleObject(pModifyThread->m_hThread,INFINITE);          char c = getch();    }     return nRetCode;}


 

上面程序的可能输出如下:

0 0 0 0 0 0 0 0 0 0

0 0 0 0 0 0 0 0 0 0

1 1 1 1 1 1 1 1 1 1

2 2 2 2 2 2 2 2 2 2

3 3 3 3 3 3 3 3 3 3

4 4 4 4 4 4 4 4 4 4

5 5 5 5 5 5 5 5 5 5

6 6 6 6 6 6 6 6 6 6

7 7 7 7 7 7 7 7 7 7

8 8 8 8 8 8 8 8 8 8

 

如果去掉程序中所有的Lock和Unlock的调用,可能的输出则如下:

0 0 0 0 0 0 0 0 0 0

1 1 1 1 1 1 0 1 1 1

2 2 2 2 2 2 2 2 2 2

3 3 3 3 3 3 3 3 3 3

4 4 4 4 4 4 4 4 4 4

5 5 5 5 5 5 5 5 5 5

6 6 6 6 6 6 6 6 6 6

6 6 7 7 7 7 7 7 7 7

8 8 8 8 8 8 8 8 7 7

9 9 9 9 9 9 9 9 9 9

注意其中的第二行,第八行和第九行的输出,这样的输出没有保证打印线程和修改线程对资源访问的完整性。也就是说出现了,修改线程和打印线程对资源访问的交叉。

 

增加了cs的Lock和Unlock操作之后,就避免了这种情况的发生。

注意:增加了cs的操作并不能保证打印操作总是在每次修改之后就进行的,想要输出:

0 0 0 0 0 0 0 0 0 0

1 1 1 1 1 1 1 1 1 1

2 2 2 2 2 2 2 2 2 2

3 3 3 3 3 3 3 3 3 3

4 4 4 4 4 4 4 4 4 4

5 5 5 5 5 5 5 5 5 5

6 6 6 6 6 6 6 6 6 6

7 7 7 7 7 7 7 7 7 7

8 8 8 8 8 8 8 8 8 8

9 9 9 9 9 9 9 9 9 9

则可以使用前面说到的CEvent,因为打印线程总是要在每次修改操作完成之后进行的。


 

热点排行