C++学习记录:多线程相关

C++学习记录:多线程相关

  之前学过一点C语言多线程方面的内容(pthread.h),但是仅仅是会用,对多线程的实现原理什么的基本上算是不了解。接下来,我的网络编程学习要进一步对代码进行优化,其中肯定少不了对多线程的运用,所以在进行下一步之前,先系统的学习一下多线程。
  本篇学习记录使用的语言为C++,调用的线程库为C++11里的std::thread库。


零、基本概念

  一个进程可以有多个线程,而一个线程只能属于一个进程。

  单核CPU的多线程执行为每条线程代码执行一段时间后进行切换,实际还是为同时进行一条线程,因为切换的速度很快,给人一种同时进行的错觉;而多核CPU的多线程执行为同时进行多条线程,当线程数量大于核数量时,也会进行线程的切换,保证线程的进行。

  多线程的执行是抢占式的,即多条线程下,没有运行顺序的规律,

一、基本线程创建 thread

如下所示,新建一个thread对象,构造函数传参第一个为线程执行函数,随后为执行函数的传参。

#include<thread>
using namespace std;

thread *t1 = new thread(work,1111);  

void work(int a)
{
    
    
	for(int n=0;n<a;n++)
	{
    
    
		printf("%d\n",a);
	}	
}

如下所示,新建一个thread数组,构造函数传参第一个为线程执行函数,随后为执行函数的传参。

#include<thread>
using namespace std;

thread *t[4];
for(int n=0;n<4;n++)
{
    
    
	t[n] = new thread(work,n);
}

void work(int a)
{
    
    
	for(int n=0;n<a;n++)
	{
    
    
		printf("%d\n",a);
	}	
}

二、等待/分离 join/detach

  join是在main函数中等待线程执行完才继续执行main函数,detach则是把该线程分离出来,不管这个线程执行得怎样,往下继续执行main函数。

  • join操作会等待线程执行完毕,然后回收该线程资源;detach操作则不会等待线程完成,线程资源的回收由init进程完成。
#include<thread>
using namespace std;

thread *t[4];
for(int n=0;n<4;n++)
{
    
    
	t[n] = new thread(work,n);
	t[n]->detach();//线程分离
	t[n]->join();//线程不分离
}

三、锁 lock/unlock

  由于线程的执行是抢占式的,且变量资源等是共享的,对于多条线程同时执行的情况下,可能对同一段内容同时执行,其中涉及到的变量操作等就会产生错误。类似同时对一个变量进行运算操作,由于赋值前的值不同,结果也就不同,导致数据出现问题。此时就可以使用锁的操作防止此类错误发生。

  • 上锁的区域同时只能被一条线程执行,由此来解决同时执行造成错误的问题。
#include<mutex>//锁的头文件
#include<thread>
using namespace std;

mutex m;//锁的变量

void work(int a)
{
    
    
	for(int n=0;n<4;n++)
	{
    
    
		m.lock();//临界区域 开始 锁掉相关区域 避免同时调用printf操作导致打印混乱
		printf("%d\n",a);
		m.unlock();//临界区域 结束 解锁
	}
} 

  但是不停的上锁和解锁很容易忘记解锁,就会出现该段代码无法被执行,导致程序出现问题,且不会报错。为了避免这种情况,我们可以使用自解锁。自解锁会自动对所处区间的代码进行上锁和解锁操作,从而防止忘解锁的情况发生。

  • 自解锁的大致原理其实就是构造器进行上锁,析构器进行解锁…
#include<mutex>//锁的头文件
#include<thread>
using namespace std;

mutex m;//锁的变量

void work(int a)
{
    
    
	for(int n=0;n<4;n++)
	{
    
    
		lock_guard<mutex>lock1(m); //自解锁
		printf("%d\n",a);
	}
} 

四、原子操作 atomic

  频繁的上锁解锁操作会非常耗时,如果上锁区域执行的代码很少的话会非常不划算。如果我们在多线程中需要对变量操作的话,频繁的给变量操作区域上下锁性价比很低,此时我们可以使用原子变量

  • 原子变量同时只能被一条线程操作,相比读写锁,速度快了不止一个量级。
#include<iostream>
#include<thread>
#include<atomic>//原子操作
using namespace std;

atomic_int count(0); //原子变量

void work(int a)
{
    
    
	for(int n=0;n<2;n++)
	{
    
    
		printf("other thread:%d\n",a);
		count++;
	}
}

int main()
{
    
    
	thread *t[4];
	for(int n=0;n<4;n++)
	{
    
    
		t[n] = new thread(work,n);
		t[n]->join();
	}
	for(int n=0;n<2;n++)
	{
    
    
		printf("main thread\n");
		count++;
	}
	while(1)
	{
    
    
		cout<<count;
		getchar();
	}
	return 0;
} 

猜你喜欢

转载自blog.csdn.net/qq_45698148/article/details/113465259