当前位置: 首页 > news >正文

多线程技术

线程

线程是计算机中最小的执行单元。

 一个进程中的所有线程共享该进程的地址空间,所以,在同一个进程中可以实现多个线程间的相互通信。

 #include<windows.h> 


创建线程: 

    

  HANDLE CreateThread(
  LPSECURITY_ATTRIBUTES lpThreadAttributes,  // 指定线程的安全属性
  SIZE_T dwStackSize,                       // initial stack size,指定为0,则新创建线程地址空间大小和调用该函数的线程地址空间大小一样
  LPTHREAD_START_ROUTINE lpStartAddress,    // thread function,指定该线程线程函数的地址。
  LPVOID lpParameter,                       // thread argument     将要传递给新建线程的命令行参数
  DWORD dwCreationFlags,                    // creation option  创建后是否立即执行。CREATE_SUSPENDED(暂停运行);0(立即运行)
  LPDWORD lpThreadId                        // thread identifier   新建线程ID号,可以设置为NULL。
);
 
例如:
HANDLE h;
h=::CreateThread(NULL,0,Proc1,NULL,0,NULL);

线程函数(需要提前声明):
DWORD WINAPI  Proc1( LPVOID  LpParameter)
{
   ...............
  return 0;
}

关闭线程句柄对象
::CloseHandle(h)  //并没有终止新建的线程,只是表示在主线程中对新创建的线程的引用不感兴趣。


 线程同步
   同一个进程中的多个线程相互协调工作达到一致性
   用户编写的程序,有时多个代码段同时读取或者修改相同地址空间中的共享数据,这个时候就需要线程同步技术来协调

 方法一:临界区对象:(关键代码段)
  临界区对象:用代码段使得资源独享
  两种方法:使用API函数和MFC类对临界区对象进行编程

    API函数操作临界区
   创建临界区:
                     CRITICAL_SECTION   Section;    //定义临界区对象
     
  临界区初始化:调用InitializeCriticalSection()对临界区对象初始化。                         
    VOID InitializeCriticalSection(
        LPCRITICAL_SECTION lpCriticalSection  // critical section
    );
               InitializeCriticalSection(&Section);  //初始化临界区对象

  进入该临界区:调用EnterCriticalSection()函数
      VOID EnterCriticalSection(
               LPCRITICAL_SECTION lpCriticalSection  // critical section
          );
        EnterCriticalSection(&Section)   //进入临界区

  释放并离开临界区:调用LeaveCriticalSection()函数
        VOID LeaveCriticalSection(
           LPCRITICAL_SECTION lpCriticalSection   // critical section
          );
        LeaveCriticalSection(&Section);   //离开临界区

   MFC使用CCriticalSection类操作临界区
         创建临界区对象:
                             CCriticalSection Section;
         
         锁定临界区:调用类成员函数Lock()
                           if(Section.Lock()){.......}      //调用锁定函数临界区,成功返回True,失败返回false
   
         释放临界区:调用类成员函数UnLock()
                       Section.Unlock();    
     
   方法二:事件对象
         事件对象是指用户在程序中使用内核对象的有无信号状态实现线程的同步。
         事件对象属于内核对象,包含以下三个成员:
              1、使用计数
              2、指明该事件是一个“自动重置的事件”还是“人工重置的事件”的布尔值。
              3、指明该事件是一个“已通知状态”和“未通知状态”的布尔值
          “人工重置事件“对象得到”信号“——>该事件的所有线程变为可调度线程
          “自动重置事件”对象得到“信号”——>该事件的线程中只有一个线程变成“可调度”

   API操作事件对象
        创建并返回事件对象:  CreateEvent()
               HANDLE CreateEvent(
  	 	LPSECURITY_ATTRIBUTES lpEventAttributes, // SD , NULL
  		BOOL bManualReset,                       // reset type  true表示为“人工重置对象”,false表示为“自动重置对象”
  		BOOL bInitialState,                      // initial state    true表示事件对象初始时为'有信号"状态,false反之
  		LPCTSTR lpName                           // object name  事件对象名称。若我NULL,则创建的是一个匿名对象
	);
    注:若bManualReset设置为true,则事件收到信号时,所有线程都可以调度,则失去了同步的意义。所以为了能够实现线程同步,一般使用自动重置false
             HANDLE hevent;
             hevent=::CreateEvent(NULL,false,true,NULL); //创建一个有信号自动重置匿名事件。
           
        手动设置为有信号状态:BOOL  SetEvent(HANDLE hEvent);
         如果创建事件对象时,将其设置为无信号状态,则用户需要手动将其设置为有信号状态。
       将事件设置为无信号状态:BOOL  ResetEvent();
                
       主动请求事件对象:   WaitForSingleObject()
            DWORD WaitForSingleObject(
			  HANDLE hHandle,        // handle to object  事件对象句柄
  			  DWORD dwMilliseconds   // time-out interval 等待事件,若为INFINITE,表示永远等待
			);
          返回值----------表用返回原因: 
                WAIT_TIMEOUT 	用户等待的时间已过
                WAIT_OBJECT_0		线程所请求的事件已经是有信号状态
                 ::WaitForSingleObject(hevent,INFINITE)   //请求事件对象

     MFC使用CEvent类实现线程同步
       CEvent类是 MFC中支持事件编程的类
       调用该类的构造函数创建对象。构造函数原型:
            CEvent( BOOL bInitiallyOwn = FALSE, BOOL bManualReset = FALSE, LPCTSTR lpszName = NULL, LPSECURITY_ATTRIBUTES lpsaAttribute = NULL );
		BOOL bInitiallyOwn 为false表示无信号状态,true表示为有信号状态。
             CEvent event(true,false,NULL,NULL);  //创建事件类对象
            
       请求事件对象:依然使用的API WaitForSingleObject();
               ::WaitForSingleObject(event.m_hObject,INFINITE); //注意,用的是event.m_hObject 
    将事件对象设置为有信号或者是无信号:
       BOOL SetEvent();
       BOOL ReSetEvent();
 
方法三:互斥对象
    互斥对象用于实现线程同步,但是互斥对象还可以在进程之间使用
        在互斥对象中包含一个线程ID和一个计数器。
        
   使用API函数操作互斥对象
         HANDLE CreateMutex(
 		 LPSECURITY_ATTRIBUTES lpMutexAttributes,  // SD    
  		 BOOL bInitialOwner,                       // initial owner   表示该互斥对象的所有者。如果为true表示,创建该互斥对象的线程拥有其所有权。false,表示创建线程不能拥有所有权
 		 LPCTSTR lpName                            // object name  互斥对象的名称。若为NULL,则表示程序创建的是匿名对象
	);
         HANDLE hmutex;  //声明互斥对象句柄
         hmutex=::CreateMutex(NULL,FALSE,NULL);  //创建互斥对象并返回其句柄
     
        释放对互斥对象的所有权:ReleaseMutex()
       线程使用完该互斥对象以后,应该调用函数ReleaseMutex释放对该互斥对象的所有权,也就是让互斥对象处于有信号状态。
        ::ReleaseMutex(hmutex);  //释放互斥对象
   
          在互斥对象中,线程也可以调用函数WaitForSingleObject()对该对象进行请求。

          注:在使用互斥对象编程时,那个线程拥有其所有权,哪个线程就应该释放互斥对象。

            

  MFC使用CMutex类为互斥对象类

       创建CMutex类对象是通过其构造函数实现的。

        CMutex( BOOL bInitiallyOwn = FALSE, LPCTSTRlpszName = NULL, LPSECURITY_ATTRIBUTES lpsaAttribute = NULL );   //  参数意义和上面相同

        CMutex mex(FLASE,NULL,NULL);  //创建互斥对象

    

        对互斥对象所保护的区域进行锁定和解锁

          virtual BOOL Lock (DWORD dwTimeout = INFINITE);

          virtual BOOL Unlock(LONG lCount,LPLONG lpPrevCount=NULL);  //其参数lCount是默认参数,用户在使用时可以不为其指定值。

          mex.Lock(INFINITE);  //锁定互斥对象

          mex.Unlock(0,0) //释放互斥对象



                  

相关文章:

  • CentOS-6.4 下成功安装openvswitch
  • 一个简单的端口扫描程序
  • 《BREW进阶与精通——3G移动增值业务运营、定制与开发》一书的网店地址
  • apue.h
  • Linux 临时mount 移动设备
  • 文件描述符、文件表项、V节点表项、dup
  • linux 第一个内核模块Hello World
  • Linux 根据端口查找进程
  • tshark命令行的使用
  • 联通iPhone,真的是狼来了?
  • GCC -L-l -D -I 参数的用法
  • 将SVN与BUG跟踪管理集成
  • Linux 内核中软中断机制
  • DAS、NAS、SAN...
  • Linux bridge 网桥模块内部数据包转发流程
  • 【RocksDB】TransactionDB源码分析
  • 【翻译】Mashape是如何管理15000个API和微服务的(三)
  • CentOS 7 防火墙操作
  • CSS 专业技巧
  • CSS相对定位
  • Laravel Telescope:优雅的应用调试工具
  • mongodb--安装和初步使用教程
  • spring学习第二天
  • 从零开始的webpack生活-0x009:FilesLoader装载文件
  • 排序(1):冒泡排序
  • 网页视频流m3u8/ts视频下载
  • 我有几个粽子,和一个故事
  • 一起参Ember.js讨论、问答社区。
  • ### Cause: com.mysql.jdbc.exceptions.jdbc4.MySQLTr
  • ###51单片机学习(1)-----单片机烧录软件的使用,以及如何建立一个工程项目
  • #{}和${}的区别是什么 -- java面试
  • #Linux(Source Insight安装及工程建立)
  • (01)ORB-SLAM2源码无死角解析-(66) BA优化(g2o)→闭环线程:Optimizer::GlobalBundleAdjustemnt→全局优化
  • (23)Linux的软硬连接
  • (bean配置类的注解开发)学习Spring的第十三天
  • (pytorch进阶之路)CLIP模型 实现图像多模态检索任务
  • (六)Hibernate的二级缓存
  • (深度全面解析)ChatGPT的重大更新给创业者带来了哪些红利机会
  • (转载)Linux 多线程条件变量同步
  • .NET Core SkiaSharp 替代 System.Drawing.Common 的一些用法
  • .NET Core中Emit的使用
  • .NET Framework 的 bug?try-catch-when 中如果 when 语句抛出异常,程序将彻底崩溃
  • .net FrameWork简介,数组,枚举
  • .NET Standard、.NET Framework 、.NET Core三者的关系与区别?
  • .net 发送邮件
  • .NET/C# 利用 Walterlv.WeakEvents 高性能地中转一个自定义的弱事件(可让任意 CLR 事件成为弱事件)
  • .NET/C# 使窗口永不激活(No Activate 永不获得焦点)
  • .NET框架
  • @Autowired和@Resource装配
  • @vue/cli 3.x+引入jQuery
  • [100天算法】-每个元音包含偶数次的最长子字符串(day 53)
  • [20150629]简单的加密连接.txt
  • [20150707]外部表与rowid.txt
  • [ABP实战开源项目]---ABP实时服务-通知系统.发布模式
  • [BZOJ1008][HNOI2008]越狱