zoukankan      html  css  js  c++  java
  • C++——多线程

    1.多进程和多线程:进程是一个总任务,一个进程可能包含多个线程。

    2.并行并发:

      并发的关键是你有处理多个任务的能力,不一定要同时。  

      并行的关键是你有同时处理多个任务的能力。  

    3.共享数据的管理和线程间的通信

    1.同步

    所谓同步,是指在不同进程之间的若干程序片断,它们的运行必须严格按照规定的某种先后次序来运行,这种先后次序依赖于要完成的特定的任务。

    如果用对资源的访问来定义的话,同步是指在互斥的基础上(大多数情况),通过其它机制实现访问者对资源的有序访问。在大多数情况下,同步已经实现了互斥,特别是所有写入资源的情况必定是互斥的。少数情况是指可以允许多个访问者同时访问资源。

    2.互斥

    所谓互斥,是指散布在不同进程之间的若干程序片断,当某个进程运行其中一个程序片段时,其它进程就不能运行它们之中的任一程序片段,只能等到该进程运行完这个程序片段后才可以运行。

    如果用对资源的访问来定义的话,互斥某一资源同时只允许一个访问者对其进行访问,具有唯一性和排它性。但互斥无法限制访问者对资源的访问顺序,即访问是无序的。

    3.互锁机制解决多线程的同步问题:只允许一个线程拥有对共享资源的独占

    参考:http://www.jizhuomi.com/software/287.html?utm_source=tuicool

    在WIN32中,同步机制主要有以下几种: 
    (1)事件(Event); 
    (2)信号量(semaphore); 
    (3)互斥量(mutex); 
    (4)临界区(Critical section)。

    InitializeCriticalSection(&Critical);               //初始化临界区对象
    hEvent = CreateEvent(NULL, FALSE, TRUE, "event");
    hSemaphore = CreateSemaphore(NULL, 1, 100, "sema");
    hMutex = CreateMutex(NULL, false, "mutex");             //创建互斥对象

    1、临界区:通过对多线程的串行化来访问公共资源或一段代码,速度快,适合控制数据访问。 

    2、互斥量:为协调共同对一个共享资源的单独访问而设计的。 

    3、信号量:为控制一个具有有限数量用户资源而设计。 

    4、事 件:用来通知线程有一些事件已发生,从而启动后继任务的开始。 

    1. 互斥量与临界区的作用非常相似,但互斥量是可以命名的,也就是说它可以跨越进程使用。所以创建互斥量需要的资源更多,所以如果只为了在进程内部是用的话使 用临界区会带来速度上的优势并能够减少资源占用量。因为互斥量是跨进程的互斥量一旦被创建,就可以通过名字打开它。 

    2. 互斥量(Mutex),信号灯(Semaphore),事件(Event)都可以被跨越进程使用来进行同步数据操作,而其他的对象与数据同步操作无关,但 对于进程和线程来讲,如果进程和线程在运行状态则为无信号状态,在退出后为有信号状态。所以可以使用WaitForSingleObject来等待进程和 线程退出。 

    3. 通过互斥量可以指定资源被独占的方式使用,但如果有下面一种情况通过互斥量就无法处理,比如现在一位用户购买了一份三个并发访问许可的数据库系统,可以根 据用户购买的访问许可数量来决定有多少个线程/进程能同时进行数据库操作,这时候如果利用互斥量就没有办法完成这个要求,信号灯对象可以说是一种资源计数 器。

    1.临界区和互斥量可是视为相同的类型,区别是临界区只能用于进程内,而互斥量可用于不同进程中不不同线程。两个都是将线程串行化,面对高并发和长代码,效率低

    2.信号量:线程并行化。

    1. CRITICAL_SECTION(临界区): 公厕管理人员每次允许一个人进入,直到他出去了,下一个人才可以进入。
    2. Event(事件): 他告诉你公厕里面当前的状态。但是,你可以闯进去。他不会管你。要干什么取决于你。
    3. Semaphore(信号量): 他允许公厕里面有N个人同时用,再多的人就必须排队。
    4. mutex(互斥量): 厕所是属于他的。他用的时候,别人决不能进去。他不用的时候,得到他的允许,别人才能进去。他也可以选择让厕所空着。


    原文:https://blog.csdn.net/liangtianmeng/article/details/81282486

    应用:

    一、并行方式的信号量在访问相同的一组资源时是最好的方法,因为它最大限度减少了系统调度线程的成本。

    二、临界区和互斥量只应用于访问串行资源(例如使用全局计数器,系统参数访问和修改)。同一进程下的线程串行化时,只应该使用临界区

    三、按指定的规则进行线程协调时使用事件。

    1.临界区(Critical Section)

    临界区(Critical Section)是一段独占对某些共享资源访问的代码,在任意时刻只允许一个线程对共享资源进行访问。

    #include "stdafx.h"
    #include<windows.h>
    #include<iostream>
    using namespace std;
    
    int number = 1; //定义全局变量
    CRITICAL_SECTION Critical;      //定义临界区句柄
    
    unsigned long __stdcall ThreadProc1(void* lp)
    {
        while (number < 100)
        {
            EnterCriticalSection(&Critical);//标识一个临界区。
            cout << "thread 1 :"<<number << endl;
            ++number;
            _sleep(100);
            LeaveCriticalSection(&Critical);//释放一个临界区
        }
    
        return 0;
    }
    
    unsigned long __stdcall ThreadProc2(void* lp)
    {
        while (number < 100)
        {
            EnterCriticalSection(&Critical);
            cout << "thread 2 :"<<number << endl;
            ++number;
            _sleep(100);
            LeaveCriticalSection(&Critical);
        }
    
        return 0;
    }
    
    int main()
    {
        InitializeCriticalSection(&Critical);   //初始化临界区对象
    
        CreateThread(NULL, 0, ThreadProc1, NULL, 0, NULL);
        CreateThread(NULL, 0, ThreadProc2, NULL, 0, NULL);
    
        Sleep(10*1000);
    
        system("pause");
        return 0;
    }

    2.事件(Event)

    事件(Event)是WIN32提供的最灵活的线程间同步方式,事件可以处于激发状态(signaled or true)或未激发状态(unsignal or false)。根据状态变迁方式的不同,事件可分为两类:
    (1)手动设置:这种对象只可能用程序手动设置,在需要该事件或者事件发生时,采用SetEvent及ResetEvent来进行设置。 //SetEvent把指定的事件对象设置为有信号状态,ResetEvent把指定的事件对象设置为无信号状态
    (2)自动恢复:一旦事件发生并被处理后,自动恢复到没有事件状态,不需要再次设置。

    使用”事件”机制应注意以下事项:
    (1)如果跨进程访问事件,必须对事件命名,在对事件命名的时候,要注意不要与系统命名空间中的其它全局命名对象冲突;
    (2)事件是否要自动恢复;
    (3)事件的初始状态设置。

    #include "stdafx.h"
    #include<windows.h>
    #include<iostream>
    using namespace std;
    
    int number = 1; //定义全局变量
    HANDLE hEvent;  //定义事件句柄
    
    unsigned long __stdcall ThreadProc1(void* lp)
    {
        while (number < 100)
        {
            WaitForSingleObject(hEvent, INFINITE);  //等待对象为有信号状态
            cout << "thread 1 :"<<number << endl;
            ++number;
            _sleep(100);
            SetEvent(hEvent);//将事件对象置为有信号状态
        }
    
        return 0;
    }
    
    unsigned long __stdcall ThreadProc2(void* lp)
    {
        while (number < 100)
        {
            WaitForSingleObject(hEvent, INFINITE);  //等待对象为有信号状态
            cout << "thread 2 :"<<number << endl;
            ++number;
            _sleep(100);
            SetEvent(hEvent);//将事件对象置为有信号状态
        }
    
        return 0;
    }
    
    int main()
    {
        CreateThread(NULL, 0, ThreadProc1, NULL, 0, NULL);
        CreateThread(NULL, 0, ThreadProc2, NULL, 0, NULL);
        hEvent = CreateEvent(NULL, FALSE, TRUE, "event");
    
        Sleep(10*1000);
    
        system("pause");
        return 0;
    }

    3.信号量

    信号量是维护0到指定最大值之间的同步对象。信号量状态在其计数大于0时是有信号的,而其计数是0时是无信号的。信号量对象在控制上可以支持有限数量共享资源的访问。

    信号量的特点和用途可用下列几句话定义:
    (1)如果当前资源的数量大于0,则信号量有效;
    (2)如果当前资源数量是0,则信号量无效;
    (3)系统决不允许当前资源的数量为负值;
    (4)当前资源数量决不能大于最大资源数量。

    //创建信号量
    HANDLE CreateSemaphore (
       PSECURITY_ATTRIBUTE psa, //信号量的安全属性
       LONG lInitialCount,     //开始时可供使用的资源数
       LONG lMaximumCount,     //最大资源数
       PCTSTR pszName);             //信号量的名称
    
    //释放信号量
    BOOL WINAPI ReleaseSemaphore(
       HANDLE hSemaphore,       //要增加的信号量句柄
       LONG lReleaseCount,     //信号量的当前资源数增加lReleaseCount
       LPLONG lpPreviousCount  //增加前的数值返回
       );
    
    
    //打开信号量 
     HANDLE OpenSemaphore (
       DWORD fdwAccess,      //access
       BOOL bInherithandle,  //如果允许子进程继承句柄,则设为TRUE
       PCTSTR pszName      //指要打开的对象的名字
      );
                         
    #include "stdafx.h"
    #include<windows.h>
    #include<iostream>
    using namespace std;
    
    int number = 1; //定义全局变量
    HANDLE hSemaphore;  //定义信号量句柄
    
    unsigned long __stdcall ThreadProc1(void* lp)
    {
        long count;
        while (number < 100)
        {
            WaitForSingleObject(hSemaphore, INFINITE);  //等待信号量为有信号状态
            cout << "thread 1 :"<<number << endl;
            ++number;
            _sleep(100);
            ReleaseSemaphore(hSemaphore, 1, &count);
        }
    
        return 0;
    }
    
    unsigned long __stdcall ThreadProc2(void* lp)
    {
        long count;
        while (number < 100)
        {
            WaitForSingleObject(hSemaphore, INFINITE);  //等待信号量为有信号状态
            cout << "thread 2 :"<<number << endl;
            ++number;
            _sleep(100);
            ReleaseSemaphore(hSemaphore, 1, &count);
        }
    
        return 0;
    }
    
    int main()
    {
        hSemaphore = CreateSemaphore(NULL, 1, 100, "sema");
    
        CreateThread(NULL, 0, ThreadProc1, NULL, 0, NULL);
        CreateThread(NULL, 0, ThreadProc2, NULL, 0, NULL);
    
        Sleep(10*1000);
    
        system("pause");
        return 0;
    }

    4.互斥量/互锁

    采用互斥对象机制。 只有拥有互斥对象的线程才有访问公共资源的权限,因为互斥对象只有一个,所以能保证公共资源不会同时被多个线程访问。互斥不仅能实现同一应用程序的公共资源安全共享,还能实现不同应用程序的公共资源安全共享。

    eg1:

    #include "stdafx.h"
    #include<windows.h>
    #include<iostream>
    using namespace std;
    
    int number = 1; //定义全局变量
    HANDLE hMutex;  //定义互斥对象句柄
    
    unsigned long __stdcall ThreadProc1(void* lp)
    {
        while (number < 100)
        {
            WaitForSingleObject(hMutex, INFINITE);
            cout << "thread 1 :"<<number << endl;
            ++number;
            _sleep(100);
            ReleaseMutex(hMutex);
        }
    
        return 0;
    }
    
    unsigned long __stdcall ThreadProc2(void* lp)
    {
        while (number < 100)
        {
            WaitForSingleObject(hMutex, INFINITE);
            cout << "thread 2 :"<<number << endl;
            ++number;
            _sleep(100);
            ReleaseMutex(hMutex);
        }
    
        return 0;
    }
    
    int main()
    {
        hMutex = CreateMutex(NULL, false, "mutex");     //创建互斥对象
    
        CreateThread(NULL, 0, ThreadProc1, NULL, 0, NULL);
        CreateThread(NULL, 0, ThreadProc2, NULL, 0, NULL);
    
        Sleep(10*1000);
    
        system("pause");
        return 0;
    }

    eg2:

    #include <iostream>   
    #include <windows.h>   
    using namespace std;
    
    HANDLE hMutex;
    
    DWORD WINAPI Fun(LPVOID lpParamter)//线程定义
    {
        while (1) {
            WaitForSingleObject(hMutex, INFINITE);//申请得到该资源:hMutex指定所申请的资源的句柄,INFINITE,表示如果没有申请到资源就一直等待该资源
            cout << "Fun display!" << endl;
            Sleep(1000);
            ReleaseMutex(hMutex);//该函数用于释放一个独占资源,进程一旦释放该资源,该资源就不再属于它了
        }
    }
    
    int main()
    {
        HANDLE hThread = CreateThread(NULL, 0, Fun, NULL, 0, NULL);//开启线程
        hMutex = CreateMutex(NULL, FALSE, L"screen"); //创造了一个名为screen并且归属于创建它的进程的资源
        CloseHandle(hThread);
        while (1) {
            WaitForSingleObject(hMutex, INFINITE);//申请得到该资源:hMutex指定所申请的资源的句柄,INFINITE,表示如果没有申请到资源就一直等待该资源
            cout << "main display!" << endl;
            Sleep(2000);
            ReleaseMutex(hMutex);//该函数用于释放一个独占资源,进程一旦释放该资源,该资源就不再属于它了
        }
    
        return 0;
    }


    ---------------------
    作者:已不再少年
    来源:CSDN
    原文:https://blog.csdn.net/s_lisheng/article/details/74278765
    版权声明:本文为博主原创文章,转载请附上博文链接!

  • 相关阅读:
    C#文件操作
    C# 本地txt文件读取至comboBox下拉列表
    自动部署war包脚本
    设置eclipse包资源管理器的背景色
    Failed to connect to ourself!错误解决办法
    Keepalived+LVS+MysqlCluster(7.1.10)架构方案(一)
    Mysql Cluster在线添加数据节点
    在Linux下使用perl通过unixODBC连接SQLServer2000
    MySQL中四舍五入的实现
    关于Temporary error: 1218错误的思考
  • 原文地址:https://www.cnblogs.com/yrm1160029237/p/10207571.html
Copyright © 2011-2022 走看看