多线程的些许理解(平台x86,具体考虑linux,windows)

                  多线程的些许理解

一.体系架构

1.原子操作 

1) 定义

 不可中断的一个或者一系列操作,也就是不会被线程调度机制打断的操作,在运行期间不会有任何的上下文切换(context switch).

2) 我们为什么关注原子操作(what)

 在多线程操作的时候,确定某个操作非原子操作,要用消耗性能的昂贵的锁去保护。

3)单核CPU、多核CPU

      在单核CPU中,能够在一个指令中完成的操作都可以看作为原子操作,因为中断只发生在指令间。

      在多核CPU中,软件级别的原子操作依赖于硬件支持的,在X86体系中,CPU提供了HLOCK pin引 线,允许CPU在执行某个指令(仅仅是一个指令)时拉低HLOCK pin引线的电平,直到这个这个指令执行完毕才放开。拉低电平导致锁住总线,如此在同一总线的CPU就暂时无法通过总线访问内存了,这样就保证了多核处理器的原子操作。(在那段时间,我猜测,处理器,在想为啥内存访问被禁止,什么鬼,交 通管制,又是哪个大领导来视察)

 4)如何定义操作原子操作

        对于非long和double基本数据类型的“简单操作”都可以看作原子操作。for example:赋值和返回。在大多数系统中long和double都占据8个字节,操作系统或者JVM(java虚拟机)很可能会将写入和读取操作分离成两个独立的操作来执行,这就产生一个读取和写入过程中的上下文切换,从而导致多线程看到不正确的情况。

        自增,自减非原子操作,一共三步(两次内存访问,一次寄存器修改)

        比如:x++;(没有招,兄弟牺牲点性能吧)

mov        eax,dword ptr [x]
add         eax,1
mov        dword ptr  [x],eax

  

二、操作系统之上的实现

    讲点没有用,有大咖测试发现,在linux上进程和线程的效率差不多,window上进程和线程效率差距很大。

1.window上多线程同步和互斥操作

        分别有四种方式:临界区(CriticalSection)、互斥对象(Mutex)、信号量(Semaphore)、事件对象(Event)。

1)临界区:

   使用函数:

CRITICAL_SECTION CriticalSection;
InitializeCriticalSection(&CriticalSection);
EnterCriticalSection(&CriticalSection);
LeaveCriticalSection(&CriticalSection);
DeleteCriticalSection(&CriticalSection);

 write an example:

#include <string>
#include <iostream>
#include < process.h>
#include <windows.h>
using namespace std;

CRITICAL_SECTION g_cs;

unsigned __stdcall threadFun(void *param)
{
     EnterCriticalSection(&g_cs);
     cout<<*(string*)(param)<<endl;
     LeaveCriticalSection(&g_cs);
     return 1;        
}

int main()
{
         InitializeCriticalSection(&g_cs);
          
         HANDLE  hth1, hth2, hth3;
         string s1 = "first", s2 = "second", s3= "third";
         
         hth1 = (HANDLE)_beginthreadex(NULL, 0, threadFun, &s1, 0, NULL);
         hth2 = (HNADLE)_beginthreadex(NULL, 0, threadFun, &s2, 0, NULL);
         hth3 = (HNADLE)_beginthreadex(NULL, 0, threadFun, &s3, 0, NULL);

         WaitForSingleObject(hth1, INFINITE);
         WaitForSingleObject(hth2, INFINITE);
         WaitForSingleObject(hth3, INFINITE);

        CloseHandle(hth1);
        CloseHandle(hth2);
        CloseHandle(hth3);
    
        DeleteCriticalSection(&g_cs); 
        return  0; 
}

2) 互斥对象(Mutex)

  引入了对象互斥对象的概念,来保证共享数据操作的完整性,每个对象对应于一个可称为互斥锁的标记,用这个标记用来保证在任一时刻,只要有一个线程访问该对象,操作接口:

CreateMutex
OpenMutex
ReleaseMutex

write an example:

#include<string>
#include<iostream>
#include<process.h>
#include<windows.h>
using namespace std;

HANDLE hmu;

unsined __stdcall threadFun(void *param)
{
      WaitForSingleObject(hmu, INFINITE);
      cout<<*(string*)(param)<<endl;
      ReleaseMutex(hmu);
      return 1;
}

int main()
{
        hmu = CreateMutex(NULL, FALSE, NULL);
        HANDLE hth1, hth2, hth3;
        string s1 = "first", s2 = "second", s3 = "third"; 

        hth1 = (HANDLE)_beginthreadex(NULL, 0, threadFun, &s1, 0, NULL);
        hth2 = (HANDLE)_beginthreadex(NULL, 0, threadFun, &s2, 0, NULL);
        hth3 = (HANDLE)_beginthreadex(NULL, 0, threadFun, &s3, 0, NULL);

        WaitForSingleObjec(hth1, INFINITE);
        WaitForSingleObjec(hth2, INFINITE);
        WaitForSingleObject(hth3, INFINITE);
 
        CloseHandle(hth1);
        CloseHandle(hth2);
        CloseHandle(hth3);

        CloseHandle(hmu); 
         return 0; }

3)信号量(Semaphore) 

      信号量,别称信号灯。负责协调各个线程,保证正确合理的使用公共资源。

     window环境接口:

CreateSemaphore
OpenSemaphore
ReleaseSemaphore

 write an example:

#include <string>
#include <iostream>
#include <process.h>
#include <windows.h>

using namespace std;

HANDLE hsem1, hsem2, hsem3;

usigned __stdcall threadFunA(void *)
{
       for(int i = 0; i < 10; i++)
       {
             WaitForSingleObject(hsem1, INFINITE);
              cout<<"A";
              ReleaseSemaphore(hsem2, 1, NULL);
        }  
        return 1;
}

unsigned __stdcall threadFunB(void*)
{
         for(int i = 0; i < 10; i++)
         {
            WaitForSingleObject(hsem2, INFINITE);
            cout<<"B";
            ReleaseSemaphore(hsem2, 1, NULL);
          }  
          return 1;
}

unsigned __stdcall threadFunC(void*)
{
       for(inti =0; i < 10; i++)
       {
           WaitForSingleObject(hsem3, INFINITE);
           cout<<"C";
           ReleaseSemphore(hsem1, 1, NULL);
        }    
        return 1;
}


int main()
{
              hsem1 = CreateSemaphore(NULL, 1, 1, NULL);
              hsem2 = CreateSemaphore(NULL, 0, 1, NULL);
              hsem3 = CreateSemaphore(NULL, 0, 1, NULL);

               HANDLE hth1, hth2, hth3;

               hth1 = (HANDLE)_beginthreadex(NULL, 0, threadFunA, NULL, 0, NULL);
              hth2  = (HANDLE)_beginthreadex(NULL, 0, threadFunB, NULL, 0, NULL);
               hth3 = (HANDLE)_beginthreadex(NULL, o, threadFunC, NULL, 0, NULl);

                WaitForSingleObject(hth1, INFINITE);
                WaitForSingleObject(hth2, INFINITE);
                 WaitForSingleObject(hth3, INFINITE);

                CloseHandle(hth1);
                CloseHandle(hth2);
                CloseHandle(hth3);
                CloseHandle(hsem1);
                CloseHandle(hsem2);
                CloseHandle(hsem3);
                return 0;
}
 

4) 事件对象(Event)

 windows事件操作

CreateEvent
OpenEvent
PulseEvent
ResetEvent
SetEvent

主线程一般可以这样写:

CreateEvent(/*...*/);    // 创建事件对象
SetEvent(/*...*/);       // 设置信号
WaitForMultiObjects(hThread, /*...*/);    // 等待线程结束
CloseHandle(/*...*/);    // 关闭线程句柄

而被启动的线程一般要等待某个事件再进行操作:

while(1){
    WaitForSingleObject(/*...*/);    // 等待事件
    /*...*/
}

总结:

       1. 使用频率,信号量 > 互斥对象 > 临界区 > 事件对象 

       2. windows编程中有两种创建线程的方法,一种为CreateThread(),另一种为_beginthreadex(),当如果代码使用标准C运行库,建议使用后者。 (window API的好)

2.linux  c 多线程编程

       无非也是锁机制,条件变量,读写锁,在linux编程中多线程和多进程差不多,无非API改改名,不做太多探讨。

       write  simple an example:

           

#include"../apue.h"
int pos=0;
char *list[10]={"person1","person2","person3","person4","person5",
		"person6","person7","person8","person9","person10"};

pthread_mutex_t mutex; 
void *func(void *p)
{
	int a = *(int*)p;	
	for(;;)
	{
		pthread_mutex_lock(&mutex);	
		if(pos>=10)
		{
			break;
		}
		printf("%d号咨询美女为%s服务
",a,list[pos]);
		pos++;	
		pthread_mutex_unlock(&mutex);
		sleep(8);//模拟服务时间
	}
	pthread_mutex_unlock(&mutex);
}
int main()
{
	int i;
	int a=1,b=2,c=3;
	pthread_t id1,id2,id3;
	pthread_mutex_init(&mutex,NULL);
	pthread_create(&id1,NULL,func,&a);
	pthread_create(&id2,NULL,func,&b);
	pthread_create(&id3,NULL,func,&c);

	pthread_join(id1,NULL);
	pthread_join(id2,NULL);
	pthread_join(id3,NULL);
	while(1);
}

  参考:网上的博客 (集百家之所长,补自己之所短)

                  《UNIX环境高级编程》这本书看完了,验证一遍,linux基本操作都应该没啥问题。

               

The future's not set,there is no fate but what we make for ourselves.
原文地址:https://www.cnblogs.com/wang1994/p/10954510.html