多线程编程入门及基本概念, 线程安全与锁,原子操作及call_once

#include
#include
#include
#include//for getpid
#include//for pthread_self
#include
#include
#include//计算效率时用到的时间 for clock_gettime

using namespace std;//实战中不要用namespace std??why
using std::cout;//推荐这样写??why??
using std::endl;
using std::string;
using std::vector;
using std::thread;
using namespace std::chrono_lierals;
namespace this_thread = std::this_thread;

std::mutex cout_lock;//cout加锁
//static long counter = 0;
//原子化
static std::atomic<long> counter{0};//原子化,就不用加锁也能保证它的正确性
std::mutex counter_lock;//counter变量加锁

class StopWatch{
	struct timespec start_ts;
	bool is_started;
public:
	void start() noexcept
	{
		clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &this->start_ts);//获取开始时间
		this->is_started = true;
	}
	long get_ns() const noexcept//得到相差纳秒数
	{
		struct timespec end_ts;//结束时间
		clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &end_ts);
		long s = end_ts.tv_sec - start_ts.tv_sec;//获取秒数
		long ns = s*1000000000L + end_ts.tv_nsec - start_ts.tv_nsec;//秒+纳秒
		return ns;
	}
};

void doit()
{
	this_thread::sleep_for(2s);
	//保证安全就加锁
	//cout_lock.lock();
	std::lock_guard<std::mutex> g(cout_lock);
	cout<<"当前进程ID"<<getpid()<<","<<pthread_self()
		<<"当前线程ID"<<this_thread::get_id()<<endl;
	cout<<a<<","<<b<<endl;
	//cout_lock.unlock();//忘记解锁的话就会死锁,--->可以直接用锁的守护

	for(int i = 0; i<1000; i++)//假装有这么一个需求,每个线程+1000
	{
		//std::lock_guard g(conuter_lock);//原子化之后就可以不用加锁
		counter++;
	}
}

//static long init_flag = 0;
static std::once_flag init_once_flag;
static std::atomic<long> init1_flag;
static void init_token()
{
	init_flag++}
void init()
{
	this_thread::sleep_for(1s);
	//if(init_flag<1)
	//{
		std::call_once(init_once_flag, init_token);
	//}
}

int main(int argc, char chonst*argv[])
{
/*
	doit(3, 4);//main thread
	thread t1{doit, 3, 4};//创建一个线程,给线程指定一个函数
	t1.join();//想要等待线程执行完成用join,不要用detatch, C++会线程自己开始,不需要start
*/	
//线程混乱的情况,输出时是不安全的
	const unsigned thread_cnt = thread::hardware_concurrency();
	const unsigned max_thread_cnt = thread_cnt*2 +1;//8*2+1
	StopWatch stopwatch;
	stopwatch.start();//开始计时
	vector<thread> workers;
	for(int i = 0; i<max_thread_cnt; i++)
	{
		thread t1(doit);//每个加1000,到第17个就是17000,要保证这个数需要加锁
		//thread t1(init)
		workers.push_back(std::move(t1));
	}
	for(auto& t : workers)
	{
		t.join();
	}
	long ns = stopwatch.get_ns();//计算时间差
	cout<<"线程数"<<max_thread_cnt<<"counter="<<counter<<"耗时"<<ns<<"ns"<<endl;
	return 0;
}

线程就是一个轻量级的进程LWP:light weight process thread
每一个进程都自带一个线程,本身进程就包含一个主线程 main thread

进程,线程,协程: linux下 posix库 clone(),都是为了隔离
协程不是操作系统的一个线程,而是用户层自己管理调度的

一开始的输出cout是不安全的,如果想要线程安全就需要用到“锁”std:mutex, 注意的是忘记解锁的话就会产生死锁,因此标准库做了一个锁的守护,在某个范围执行完毕之后就会自己去释放std::lock_guard g()

原子操作:std::atomic<>
另外,call_once:在多线程编程中,有时某个任务只需要执行一次,此时可以用std::call_once函数配合std::once_flag来实现。如果多个线程需要同时调用某个函数,std::call_once可以保证多个线程对该函数只调用一次。也可用在解决线程安全的单例模式。例std::call_once(init_once_flag, 函数名));

C++11中的move,移动,pure, rvalue,值语义
std::move是将对象的状态或者所有权从一个对象转移到另一个对象,只是转移,没有内存的搬迁或者内存拷贝。

关于using namespace std
多线程编程入门及基本概念, 线程安全与锁,原子操作及call_once_第1张图片
多线程编程入门及基本概念, 线程安全与锁,原子操作及call_once_第2张图片

你可能感兴趣的:(多线程)