栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > C/C++/C#

C11 多线程初学1

C/C++/C# 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

C11 多线程初学1

C++ 11 多线程编程
  • 1、线程的创建和使用
    • 1.1 创建线程
    • 1.2 线程的使用
  • 2.实现线程同步
      • 2.1互斥锁
      • 2.2 条件变量
  • 3.例
    • 1:两线程交替打印奇偶数

1、线程的创建和使用

引入线程库,其提供了thread类,创建一个线程即实例化一个该类的对象,以此进行线程的创建和使用。

#include
using namespce std;
1.1 创建线程

一个线程可以用一个 thread 类的对象来表示,thread类中重载了构造函数和移动拷贝构造

#include
#include

using namespace std;

void funa()
{
	cout << "thread_funa()" << endl;
}
int main()
{
	//创建线程——构造函数
	thread a(funa);
	cout << a.get_id() << endl;
	//移动拷贝构造
	thread b(std::move(a));
	cout << b.get_id() << endl;

	b.join();
	//移动拷贝构造进行资源的交换,所有要注释掉
	//a.join();
	cout << "main end" << endl;

}

注意: thread类无拷贝构造函数、无赋值运算符,即不能直接用一个事先定义好的thread对象拷贝构造另一个thread对象,也不能不能进行赋值操作。但可以将临时的thread对象赋值给另一个thread即移动拷贝构造,也可以移动赋值。

1.2 线程的使用

thread 提供的函数: 线程支持库
thread类常用的成员函数:

成员函数含义
get_id()获取当前thread对象的线程id
joinable()判断当前线程是否活跃(是:true;否:false)即是否支持调用 join()
join()阻塞当前thread 对象所在的线程,直至thread 对象表示的线程执行完毕
detach()将当前线程从调用该函数的线程中分离出去,他们彼此独立运行
swap()交换两个线程的状态

注意:每个thread 对象在调用析构销毁前,要么调用 join() 函数令主线程等待子线程执行完毕,要么调用detach() 函数将子线程和主线程分离,二者必选一,否则存在以下问题:

  • 线程占用的资源无法全部释放,造成内存泄露
  • 当主线程执行完毕而子线程未完时,程序执行引发异常。

在this_thread 命名空间的部分函数:

函数含义
get_id()获得当前线程的 ID
yield()阻塞当前线程让出cpu,直至条件成熟
sleep_for()阻塞当前线程指定的时间
sleep_until()阻塞当前线程,直至某个时间点为止

例:

//休眠1000毫秒,1s
this_thread::sleep_for(chrono::milliseconds(1000));
2.实现线程同步 2.1互斥锁

互斥锁用 mutex 类(位于std命名空间中)的对象表示,定义在头文件头文件中。
成员函数:lock()——加锁; unlock()——解锁
例:对临界资源变量n的访问

#include
using namespcase std;
mutex m; //实例化mutex对象m
void Show_n()
{
	m.lock();
	cout << n << endl;
	m.unlock();
}

实现严格基于作用域的互斥体所有权包装器:lock_fuard
原理:创建 lock_guard 对象时,它试图接收给定互斥的所有权。控制离开创建 lock_guard 对象的作用域时,销毁 lock_guard 并释放互斥。即,创建即加锁,作用域结束自动解锁。

#include
using namespcase std;
mutex m; //实例化mutex对象m
void Show_n()
{
	lock_guard g1(m);
	cout << n << endl;
}
//作用域结束,析构g1,m解锁

lock_gurad传入两个参数,第二个为adopt_lock标识时,表示该互斥量之前必须已经lock,才能使用此参数,故需要提前手动锁定。

#include
using namespcase std;
mutex m; //实例化mutex对象m
void Show_n()
{
	m.lock(); //手动锁定
	lock_guard g1(m, adopt_lock);
	cout << n << endl;
}
//作用域结束,析构g1,m解锁

实现可移动的互斥体所有权包装器:unique_lock
与lock_guard类似,用法更丰富。

成员函数含义
lock()锁定
try_lock()尝试锁定,若不能,先去执行其他代码并返回false;可以,进行加锁并返回true
unlock()解锁

unique_lock与lock_guard

unique_locklock_guard
手动lock、unlock支持不支持
参数支持 adopt_lock/try_to_lock/defer_lock支持 adopt_lock

try_to_lock: 尝试用mutx的lock()去锁定这个mutex,但如果没有锁定成功,会立即返回,不会阻塞在那里
defer_lock: 这个参数表示暂时先不lock,之后手动去lock,但是使用之前也是不允许去lock。

2.2 条件变量

C11提供的两种表示条件变量的类:
都定义在头文件中,常与互斥锁搭配使用,避免线程间抢夺资源。

  • condition_variable
    该类表示的条件变量只能和unique_lock类表示的互斥锁搭配使用;
  • condition_variable_any
    该类表示的条件变量可以与任意类型的互斥锁搭配使用(如:递归互斥锁、定时互斥锁等)

条件变量常用的成员函数

成员函数功能
wait()阻塞当前线程,等待条件成立
wait_for()阻塞当前线程的过程中,该函数会自动调用unlock()解锁,令其他线程使用公共资源。当条件成立或超过指定的等待时间,该函数自动调用lock()加锁,同时令线程继续执行
wait_until()与wait_for() 类似,区别是其可以设定一个具体的时间点,当条件成立或等待时间超过了指定的时间点,函数自动加锁,线程执行
notify_one()唤醒一个等待的线程
notify_all()唤醒所有等待线程

关于wait() :
调用wait()1先获得mutex,2线程被阻塞,当wait陷入休眠是会自动释放mutex。直到另外某个线程调用 notify_one或notify_all唤醒了当前线程。当线程被唤醒时,此时线程是已经自动占有了mutex。

3.例 1:两线程交替打印奇偶数
#include
#include
#include
using namespace std;
std::mutex data_mutex;
std::condition_variable data_var;
bool tag = true;
// odd : 1 
// even: 2
// odd : 3
// even : 4

void printodd() // 打印奇数
{
	std::unique_lock ulock(data_mutex);
	for (int odd = 1; odd <= 100; odd += 2)
	{
		while (!tag)
		{
			data_var.wait(ulock);
		}
		cout << "odd: " << odd << endl;
		tag = false;
		data_var.notify_all();
	}
}
void printeven() //打印偶数
{
	std::unique_lock ulock(data_mutex);

	for (int even = 2; even <= 100; even += 2)
	{	
		while (tag)
		{
			data_var.wait(ulock);//  1 2 
		}
		cout << "even: " << even << endl;
		tag = true;
		data_var.notify_all();
	}
}


int main()
{
	thread thodd(printodd);
	thread theven(printeven);

	thodd.join();
	theven.join();
	return 0;
}

参考文献:
C++多线程unique_lock详解

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/657500.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号