//示例代码:
CStringArray g_ArrString;
UINT __cdecl ThreadProc(LPVOID lpParameter)
{
 int startIdx = (int)lpParameter;
 for (int idx = startIdx; idx < startIdx+100; ++idx) {
  CString str;
  str.Format(_T("%d"), idx);
  g_ArrString.Add(str);
 }
 return 0;
}
 
void CThreadTestDlg::OnBnClickedBtn()
{
 for (int idx = 1; idx <= 50; ++idx) {
  AfxBeginThread(ThreadProc, (LPVOID)(idx*10));
 }
}
 
void CThreadTestDlg::OnBnClickedPrintBtn()
{
 CString strCount;
 INT_PTR nCount = g_ArrString.GetCount();
 strCount.Format(_T("%d"), nCount);
 MessageBox(strCount);
 
 for (INT_PTR idx = 0; idx < nCount; ++idx) {
  OutputDebugString(g_ArrString.GetAt(idx));
 }
}
 
///////////////////////////////////////////////////////////////////////////////
 
①、Mutex(互斥器)
 
使用方法:
1、创建一个互斥器:CreateMutex;
2、打开一个已经存在的互斥器:OpenMutex;
3、获得互斥器的拥有权:WaitForSingleObject、WaitForMultipleObjects 等一类等待的函数……(可能造成阻塞);
4、释放互斥器的拥有权:ReleaseMutex;
5、关闭互斥器:CloseHandle;
 
HANDLE ghMutex = NULL;
CStringArray g_ArrString;
UINT __cdecl ThreadProc(LPVOID lpParameter)
{
 int startIdx = (int)lpParameter;
 for (int idx = startIdx; idx < startIdx+100; ++idx) {
  CString str;
  str.Format(_T("%d"), idx);
 
  DWORD dwWaitResult = WaitForSingleObject(ghMutex, INFINITE);
  switch (dwWaitResult)
  {
  case WAIT_ABANDONED:
  case WAIT_OBJECT_0:
   g_ArrString.Add(str);
   ReleaseMutex(ghMutex);
   break;
  }
  //g_ArrString.Add(str);
 }
 return 0;
}
 
void CThreadTestDlg::OnBnClickedBtn()
{
 ghMutex = CreateMutex(NULL, FALSE, NULL);
 for (int idx = 1; idx <= 50; ++idx) {
  AfxBeginThread(ThreadProc, (LPVOID)(idx*10));
 }
}
 
void CThreadTestDlg::OnBnClickedPrintBtn()
{
 CString strCount;
 INT_PTR nCount = g_ArrString.GetCount();
 strCount.Format(_T("%d"), nCount);
 MessageBox(strCount);
 
 for (INT_PTR idx = 0; idx < nCount; ++idx) {
  OutputDebugString(g_ArrString.GetAt(idx));
 }
 
 CloseHandle(ghMutex);
}
 
※ 命名标准:Mutex 可以跨进程使用,所以其名称对整个系统而言是全局的,所以命名不要过于普通,类似:Mutex、Object 等。
最好想一些独一无二的名字等!
 
固有特点(优点+缺点):
1、是一个系统核心对象,所以有安全描述指针,用完了要 CloseHandle 关闭句柄,这些是内核对象的共同特征;
2、因为是核心对象,所以执行速度会比 Critical Sections 慢几乎100倍的时间(当然只是相比较而言);
3、因为是核心对象,而且可以命名,所以可以跨进程使用;
4、Mutex 使用正确的情况下不会发生死锁;
5、在“等待”一个 Mutex 的时候,可以指定“结束等待”的时间长度;
6、可以检测到当前拥有互斥器所有权的线程是否已经退出!Wait……函数会返回:WAIT_ABANDONED
===================================================
②、Semaphores(信号量)
 
租车例子的比喻很恰当!
 
使用方法:
1、创建一个信号量:CreateSemaphore;
2、打开一个已经存在的信号量:OpenSemaphore;
3、获得信号量的一个占有权:WaitForSingleObject、WaitForMultipleObjects 等一类等待的函数……(可能造成阻塞);
4、释放信号量的占有权:ReleaseSemaphore;
5、关闭信号量:CloseHandle;
 
HANDLE ghSemaphore = NULL;
UINT __cdecl ThreadProc(LPVOID lpParameter)
{
 int startIdx = (int)lpParameter;
 CString strOut;
 while(TRUE) {
 
  DWORD dwWaitResult = WaitForSingleObject(ghSemaphore, 0);
  switch (dwWaitResult)
  {
  case WAIT_OBJECT_0:
   strOut.Format(_T("Thread %d: wait succeeded !"), GetCurrentThreadId());
   OutputDebugString(strOut);
   ReleaseSemaphore(ghSemaphore, 1, NULL);
 
   break;
  case WAIT_TIMEOUT:
   strOut.Format(_T("Thread %d: wait timed out !"), GetCurrentThreadId());
   OutputDebugString(strOut);
   break;
  }
 }
 return 0;
}
 
void CThreadTestDlg::OnBnClickedBtn()
{
 ghSemaphore = CreateSemaphore(NULL, 10, 10, NULL);
 for (int idx = 1; idx <= 20; ++idx) {
  AfxBeginThread(ThreadProc, (LPVOID)(idx*10));
 }
}
 
void CThreadTestDlg::OnBnClickedPrintBtn()
{
 CloseHandle(ghSemaphore);
}
 
※ 命名标准:Semaphores 可以跨进程使用,所以其名称对整个系统而言是全局的,所以命名不要过于普通,类似:Semaphore、Object 等。
最好想一些独一无二的名字等!
 
固有特点(优点+缺点):
1、是一个系统核心对象,所以有安全描述指针,用完了要 CloseHandle 关闭句柄,这些是内核对象的共同特征;
2、因为是核心对象,所以执行速度稍慢(当然只是相比较而言);
3、因为是核心对象,而且可以命名,所以可以跨进程使用;
4、Semaphore 使用正确的情况下不会发生死锁;
5、在“等待”一个 信号量 的时候,可以指定“结束等待”的时间长度;
6、非排他性的占有,跟 Critical Sections 和 Mutex 不同,这两种而言是排他性占有,
即:同一时间内只能有单一线程获得目标并拥有操作的权利,而 Semaphores 则不是这样,
同一时间内可以有多个线程获得目标并操作!
 
所以,这里面问大家一个问题,如果还是用信号量的方式去做之前向 CStringArray 中添加节点的同步可以吗?
===================================================
③、Event Objects(事件)
 
 Event 方式是最具弹性的同步机制,因为他的状态完全由你去决定,不会像 Mutex 和 Semaphores 的状态会由类似:
 WaitForSingleObject 一类的函数的调用而改变,所以你可以精确的告诉 Event 对象该做什么事?以及什么时候去做!
 
 使用方法:
1、创建一个事件对象:CreateEvent;
2、打开一个已经存在的事件对象:OpenEvent;
3、获得事件的占有权:WaitForSingleObject 等函数(可能造成阻塞);
4、释放事件的占有权(设置为激发(有信号)状态,以让其他等待中的线程苏醒):SetEvent;
5、手动置为非激发(无信号)状态:ResetEvent
6、关闭事件对象句柄:CloseHandle;
 
固有特点(优点+缺点):
1、是一个系统核心对象,所以有安全描述指针,用完了要 CloseHandle 关闭句柄,这些是内核对象的共同特征;
2、因为是核心对象,所以执行速度稍慢(当然只是相比较而言);
3、因为是核心对象,而且可以命名,所以可以跨进程使用;
4、通常被用于 overlapped I/O 或被用来设计某些自定义的同步对象。
 
#include <windows.h>
#include <stdio.h>
 
#define THREADCOUNT 4
 
HANDLE ghGlobalWriteEvent;
HANDLE ghReadEvents[THREADCOUNT];
 
DWORD WINAPI ThreadProc(LPVOID);
 
void CreateEventsAndThreads(void)
{
    HANDLE hThread;
    DWORD i, dwThreadID;
 
    // Create a manual-reset event object. The master thread sets
    // this to nonsignaled when it writes to the shared buffer.
 
    ghGlobalWriteEvent = CreateEvent(
        NULL, // default security attributes
        TRUE, // manual-reset event
        TRUE, // initial state is signaled
        TEXT("WriteEvent") // object name
        );
 
    if (ghGlobalWriteEvent == NULL)
    {
        printf("CreateEvent failed (%d)\n", GetLastError());
        return;
    }
    else if ( GetLastError() == ERROR_ALREADY_EXISTS )
    {
        printf("Named event already exists.\n");
        return;
    }
 
    // Create multiple threads and an auto-reset event object
    // for each thread. Each thread sets its event object to
    // signaled when it is not reading from the shared buffer.
 
    for(i = 0; i < THREADCOUNT; i++)
    {
        // Create the auto-reset event
        ghReadEvents[i] = CreateEvent(
            NULL, // no security attributes
            FALSE, // auto-reset event
            TRUE, // initial state is signaled
            NULL); // object not named
 
        if (ghReadEvents[i] == NULL)
        {
            printf("CreateEvent failed (%d)\n", GetLastError());
            return;
        }
 
        hThread = CreateThread(NULL,
            0,
            ThreadProc,
            &ghReadEvents[i], // pass event handle
            0,
            &dwThreadID);
 
        if (hThread == NULL)
        {
            printf("CreateThread failed (%d)\n", GetLastError());
            return;
        }
    }
}
 
void WriteToBuffer(VOID)
{
    DWORD dwWaitResult, i;
 
    // Reset ghGlobalWriteEvent to nonsignaled, to block readers
 
    if (! ResetEvent(ghGlobalWriteEvent) )
    {
        printf("ResetEvent failed (%d)\n", GetLastError());
        return;
    }
 
    // Wait for all reading threads to finish reading
 
    dwWaitResult = WaitForMultipleObjects(
        THREADCOUNT, // number of handles in array
        ghReadEvents, // array of read-event handles
        TRUE, // wait until all are signaled
        INFINITE); // indefinite wait
 
    switch (dwWaitResult)
    {
        // All read-event objects were signaled
        case WAIT_OBJECT_0:
            // TODO: Write to the shared buffer
            printf("Main thread writing to the shared buffer...\n");
            break;
 
        // An error occurred
        default:
            printf("Wait error: %d\n", GetLastError());
            ExitProcess(0);
    }
 
    // Set ghGlobalWriteEvent to signaled
 
    if (! SetEvent(ghGlobalWriteEvent) )
    {
        printf("SetEvent failed (%d)\n", GetLastError());
        ExitProcess(0);
    }
 
    // Set all read events to signaled
    for(i = 0; i < THREADCOUNT; i++)
        if (! SetEvent(ghReadEvents[i]) )
        {
            printf("SetEvent failed (%d)\n", GetLastError());
            return;
        }
}
 
void CloseEvents()
{
    int i;
 
    for( i=0; i < THREADCOUNT; i++ )
        CloseHandle(ghReadEvents[i]);
 
    CloseHandle(ghGlobalWriteEvent);
}
 
void main()
{
    int i;
 
    // TODO: Create the shared buffer
 
    // Create the events and THREADCOUNT threads to read from the buffer
 
    CreateEventsAndThreads();
 
    // Write to the buffer three times, just for test purposes
 
    for(i=0; i < 3; i++)
        WriteToBuffer();
 
    // Close the events
 
    CloseEvents();
}
 
DWORD WINAPI ThreadProc(LPVOID lpParam)
{
    DWORD dwWaitResult;
    HANDLE hEvents[2];
 
    hEvents[0] = *(HANDLE*)lpParam; // thread's read event
    hEvents[1] = ghGlobalWriteEvent; // global write event
 
    dwWaitResult = WaitForMultipleObjects(
        2, // number of handles in array
        hEvents, // array of event handles
        TRUE, // wait till all are signaled
        INFINITE); // indefinite wait
 
    switch (dwWaitResult)
    {
        // Both event objects were signaled
        case WAIT_OBJECT_0:
            // TODO: Read from the shared buffer
            printf("Thread %d reading from buffer...\n",
                   GetCurrentThreadId());
            break;
 
        // An error occurred
        default:
            printf("Wait error: %d\n", GetLastError());
            ExitThread(0);
    }
 
    // Set the read event to signaled
 
    if (! SetEvent(hEvents[0]) )
    {
        printf("SetEvent failed (%d)\n", GetLastError());
        ExitThread(0);
    }
 
    return 1;
}
 
===================================================
※※※ 小作业:
MFC中同样对 Mutex、Semaphores、Event 进行了封装,所以尝试使用 CMutex、CSemaphore、CEvent 等类进行线程间的同步将更为方便!
------------------------------------- End -------------------------------------------

vc++高级班之多线程篇[7]---线程间的同步机制②的更多相关文章

  1. vc++高级班之多线程篇[6]---线程间的同步机制①

    ①.线程同步的必要性:   int g_Num = 0; UINT __cdecl ThreadProc(LPVOID lpParameter) {  for (int idx = 0; idx &l ...

  2. iOS开发多线程篇 04 —线程间的通信

    iOS开发多线程篇—线程间的通信 一.简单说明 线程间通信:在1个进程中,线程往往不是孤立存在的,多个线程之间需要经常进行通信 线程间通信的体现 1个线程传递数据给另1个线程 在1个线程中执行完特定任 ...

  3. vc++高级班之窗口篇[4]---让程序只运行一个实例

      大家都看过或者使用过类似只运行一个实例的程序,比如:QQ游戏.部分浏览器 等等! 让一个程序只运行一个实例的方法有多种,但是原理都类似,也就是在程序创建后,有窗口的程序在窗口创建前, 检查系统中是 ...

  4. Linux 多线程 - 线程异步与同步机制

    Linux 多线程 - 线程异步与同步机制 I. 同步机制 线程间的同步机制主要包括三个: 互斥锁:以排他的方式,防止共享资源被并发访问:互斥锁为二元变量, 状态为0-开锁.1-上锁;开锁必须由上锁的 ...

  5. iOS开发多线程篇—创建线程

    iOS开发多线程篇—创建线程 一.创建和启动线程简单说明 一个NSThread对象就代表一条线程 创建.启动线程 (1) NSThread *thread = [[NSThread alloc] in ...

  6. Java并发之线程间的同步协作与通信协作

    1,Monitor监视器与syncrhoized实现原理 1.1:Monitor Monitor是一个同步工具,相当于操作系统中的互斥量(mutex),即值为1的信号量. 它内置与每一个Object对 ...

  7. iOS开发多线程篇 03 —线程安全

    iOS开发多线程篇—线程安全 一.多线程的安全隐患 资源共享 1块资源可能会被多个线程共享,也就是多个线程可能会访问同一块资源 比如多个线程访问同一个对象.同一个变量.同一个文件 当多个线程访问同一块 ...

  8. swift开发多线程篇 - NSThread 线程相关简单说明(一些使用和注意点)

    一 说明 本文涉及代码可以从https://github.com/HanGangAndHanMeimei/Code地址获得. 二 NSThread的基本使用和创建 1)基本用法(主线程|当前线程) 1 ...

  9. java多线程5:线程间的通信

    在多线程系统中,彼此之间的通信协作非常重要,下面来聊聊线程间通信的几种方式. wait/notify 想像一个场景,A.B两个线程操作一个共享List对象,A对List进行add操作,B线程等待Lis ...

随机推荐

  1. zookeeper的搭建和简单的使用

    一.什么是zookeeper,有什么用 ZooKeeper是一个分布式的,开放源码的分布式应用程序协调服务,是Google的Chubby一个开源的实现,它是集群的管理者,监视着集群中各个节点的状态根据 ...

  2. Spring Boot笔记八:表单验证

    所谓的表单验证,就是为了防止用户乱输入的,这个问题前端的HTML5就可以判断了,其实不需要后端来验证,这里还是讲一下后端验证 首先,我们的Person类,我们加上一些表单验证的注释,如下: packa ...

  3. python脚本难点

    本文主要记录在录制过程中,遇到一些需要特殊处理的脚本.如果有总结不好的地方,希望多多指点. 一.输入框listview选择: 如图:   类似这种情况,选择其中一项的脚本如下: m = driver. ...

  4. 转---redshift database ---学习

    摘自他人 前沿 根据最近一段时间对redshift的研究,发现一些特性比较适合我们当前的业务. 1 比如它的快速恢复能力,因为这一点,我们可以尽量在redshit里面存放一定生命周期的数据,对过期的数 ...

  5. Python线程和协程-day10

    写在前面 上课第10天,打卡: 感谢Egon老师细致入微的讲解,的确有学到东西! 一.线程 1.关于线程的补充 线程:就是一条流水线的执行过程,一条流水线必须属于一个车间: 那这个车间的运行过程就是一 ...

  6. C#中属性的使用——主动调用才发挥作用

    微软对属性定义如下: “属性是这样的成员:它提供灵活的机制来读取.编写或计算某个私有字段的值. 可以像使用公共数据成员一样使用属性,但实际上它们是称作“访问器”的特殊方法. 这使得可以轻松访问数据,此 ...

  7. Windows Server 2008 R2中无法使用360免费Wifi的解决方案

    为了使主机和虚拟机在同一个无线网络中,而虚拟机的系统是Windows Server 2008 R2 64位的,使用360免费wifi,始终无法开启.在网上查找解决方案,终于找到了原因:Windows ...

  8. js 对象及空对象或数组及空数组的判断与比较

    工作中经常会使用到,这里记录一下 判断是不是对象: let obj = {}; Object.prototype.toString.call(obj) == "[object Object] ...

  9. pom样板

    <properties> <java.version>1.6</java.version> <project.build.sourceEncoding> ...

  10. (5)top k大的数目

    一.问题 在一个很长的数组中,求出top k大小的数目 二.办法 用优先队列 时间复杂度O(nlog(k)),应该是最差的情况下是这个 三.Code package algorithm; import ...