C++:线程支持库 的介绍和使用

C++:线程支持库 的介绍和使用,第1张

文章目录
  • thread
    • 1. 构造函数
    • 2. join()
    • 3. detach()
    • 4. joinable()
  • 多线程下的全局变量
    • 1. 加入互斥量std::metux
    • 2. 条件变量

thread

定义在头文件#include

类 thread 表示单个执行线程。线程允许多个函数同时执行。
std::thread 对象也可能处于不表示任何线程的状态(默认构造、被移动、 detach 或 join 后),并且执行线程可能与任何 thread 对象无关( detach 后)。

没有两个 std::thread 对象会表示同一执行线程; std::thread 不是可复制构造 (CopyConstructible) 或可复制赋值 (CopyAssignable) 的,尽管它可移动构造 (MoveConstructible) 且可移动赋值 (MoveAssignable) 。

1. 构造函数

thread() noexcept; //(1) (C++11 起) 

thread( thread&& other ) noexcept; //(2) (C++11 起) 

template< class Function, class... Args >
explicit thread( Function&& f, Args&&... args ); //(3) (C++11 起) 

thread( const thread& ) = delete; // (4) (C++11 起) 

构造新的 thread 对象。

  1. 构造不表示线程的新 thread 对象。

  2. 移动构造函数。构造表示曾为 other 所表示的执行线程的 thread 对象。此调用后 other 不再表示执行线程。

  3. 构造新的 std::thread 对象并将它与执行线程关联。新的执行线程开始执行 。

示例:构造四个线程,挂接四个函数

void funa()
{
	cout << "funa()" << endl;
}
void funb(int a)
{
	a += 1;
	cout << "funb(int a)" << endl;
}
void func(int* a)
{
	*a += 10;
	cout << "func(int* a)" << endl;
}
void fund(int& a)
{
	a += 20;
	cout << "fund(int& a)" << endl;
}

int main(void)
{
	int x = 10;
	std::thread tha(funa);
	std::thread thb(funb, x);
	std::thread thc(func, &x);
	std::thread thd(fund, std::ref(x));
	
	cout << "main end" << endl;
	return 0;
}

这样运行是会报错的,因为主函数提前结束,而构造的线程还没执行完。

2. join()

所以需要引入join()方法,意思是等待线程完成其 *** 作:

void join();

阻塞当前线程直至 *this 所标识的线程结束其执行。

*this 所标识的线程的完成同步于对应的从 join() 成功返回。

*this 自身上不进行同步。同时从多个线程在同一 thread 对象上调用 join() 构成数据竞争,导致未定义行为。

主函数加入等待函数后,就可以正常执行。

int main(void)
{
	int x = 10;
	std::thread tha(funa);
	std::thread thb(funb, x);
	std::thread thc(func, &x);
	std::thread thd(fund, std::ref(x));
	tha.join();
	thb.join();
	thc.join();
	thd.join();
	cout << "main end" << endl;
	return 0;
}

3. detach()

容许线程从线程句柄独立开来执行

void detach();

从 thread 对象分离执行线程,允许执行独立地持续。一旦该线程退出,则释放任何分配的资源。
调用 detach 后 *this 不再占有任何线程。

4. joinable()

检查线程是否可合并,即潜在地运行于平行环境中 .

bool joinable() const noexcept;

检查 std::thread 对象是否标识活跃的执行线程。具体而言,若 get_id() != std::thread::id() 则返回 true 。故默认构造的 thread 不可结合。

结束执行代码,但仍未结合的线程仍被当作活跃的执行线程,从而可结合。

示例:

void foo()
{
	std::this_thread::sleep_for(std::chrono::seconds(1));
	cout << "foo end" << endl;
}

int main(void)
{
	std::thread tha;
	cout << "Before starting , joinable: " << tha.joinable() << endl;
	tha = std::thread(foo);
	cout << "After starting, joinable: " << tha.joinable() << endl;
	tha.join();
	cout << "After joining, joinable: " << tha.joinable() << endl;

	return 0;
}

多线程下的全局变量

如果声明一个整型全局变量,再利用多个线程对其进行++ *** 作,是否会得到预期的答案?

#include

int g_num = 0;
void Print(int id)
{
	for (int i = 0; i < 5; ++i)
	{
		++g_num;
		cout << "id: " << id << " ==> " << g_num << endl;
		std::this_thread::sleep_for(std::chrono::seconds(1));
	}
}

int main(void)
{
	std::thread th1(Print, 1);
	std::thread th2(Print, 2);
	th1.join();
	th2.join();
	cout << "main end" << endl;

	return 0;
}

1. 加入互斥量std::metux
  1. 锁定互斥,若互斥不可用则阻塞
void lock();

锁定互斥。若另一线程已锁定互斥,则到 lock 的调用将阻塞执行,直至获得锁。

若 lock 为已占有 mutex 的线程调用,则行为未定义:例如,程序可能死锁。鼓励能检测非法使用的实现抛出以 resource_deadlock_would_occur 为错误条件的 std::system_error ,而不是死锁。

同一互斥上先前的 unlock() *** 作同步于(定义于 std::memory_order )此 *** 作。

  1. 解锁互斥
    互斥必须为当前执行线程所锁定,否则行为未定义。
    此 *** 作同步于(定义于 std::memory_order )任何后继的取得同一互斥所有权的锁 *** 作。

更改上述示例:对自增 *** 作加锁

#include
#include

std::mutex mtx;
int g_num = 0;
void Print(int id)
{
	for (int i = 0; i < 5; ++i)
	{
		mtx.lock();
		++g_num;
		cout << "id: " << id << " ==> " << g_num << endl;
		mtx.unlock();

		std::this_thread::sleep_for(std::chrono::seconds(1));
	}
}

int main(void)
{
	std::thread th1(Print, 1);
	std::thread th2(Print, 2);
	th1.join();
	th2.join();
	cout << "main end" << endl;

	return 0;
}

运行结果:

互斥量的特点简单理解就是:对于临界资源,只有被锁住的线程可以访问,其他的线程不可以访问。

2. 条件变量

包含在头文件#include

condition_variable 类是同步原语,能用于阻塞一个线程,或同时阻塞多个线程,直至另一线程修改共享变量(条件)并通知 condition_variable 。

有意修改变量的线程必须
1.获得 std::mutex (常通过 std::lock_guard )
2.在保有锁时进行修改
3.在 std::condition_variable 上执行 notify_one 或 notify_all .

示例1:

#include
#include
#include
#include
#include
using namespace std;

std::condition_variable cv;
std::mutex mtx;
int isReady = 0;
const int n = 10;
void Print_A()
{
	std::unique_lock<std::mutex> lock(mtx);
	int i = 0;
	while (i < n)
	{
		while (isReady != 0)
		{
			cv.wait(lock);
		}
		cout << "A" << endl;
		isReady = 1;
		++i;
		std::this_thread::sleep_for(std::chrono::milliseconds(100));
		cv.notify_all();
	}
}

void Print_B()
{
	std::unique_lock<std::mutex> lock(mtx);
	int i = 0;
	while (i < n)
	{
		while (isReady != 1)
		{
			cv.wait(lock);
		}
		cout << "B" << endl;
		isReady = 2;
		++i;
		std::this_thread::sleep_for(std::chrono::milliseconds(100));
		cv.notify_all();
	}
}

void Print_C()
{
	std::unique_lock<std::mutex> lock(mtx);
	int i = 0;
	while (i < n)
	{
		while (isReady != 2)
		{
			cv.wait(lock);
		}
		cout << "C" << endl;
		isReady = 0;
		++i;
		std::this_thread::sleep_for(std::chrono::milliseconds(100));
		cv.notify_all();
	}
}

int main(void)
{
	thread tha(Print_A);
	thread thb(Print_B);
	thread thc(Print_C);
	tha.join();
	thb.join();
	thc.join();

	return 0;
}

先来介绍出现的函数:

  1. wait() —— 阻塞当前线程,直到条件变量被唤醒
void wait( std::unique_lock<std::mutex>& lock );

wait 导致当前线程阻塞直至条件变量被通知,或虚假唤醒发生,可选地循环直至满足某谓词(bool值)。

原子地解锁 lock ,阻塞当前执行线程,并将它添加到于 *this 上等待的线程列表。线程将在执行 notify_all() 或 notify_one() 时被解除阻塞。解阻塞时,无关乎原因, lock 再次锁定且 wait 退出。

  1. notify_all() —— 通知所有等待的线程
    解阻塞全部当前等待于 *this 的线程

那么图示为:
1.三个线程都处于就绪状态

2.假设线程C先获得互斥量,那么C运行,循环时判断 isReady是否等于2,不等于2,那么将当前线程C挂起到条件变量的阻塞队列中(线程列表),并释放互斥量;释放后假设 A B两线程,B先获得互斥量,那么B运行,循环判断isReady是否为1,不为1,那么把B挂起到阻塞队列,释放互斥量;然后只剩A线程,A线程直接退出循环,让isReady为1,然后唤醒所有线程。

3.唤醒 B C线程后,线程A还在继续运行,再次循环时判断 isready是否为0,不为0,所以线程A挂起到线程列表里,并释放互斥量;假设C先获得互斥量,循环时又被放入线程列表,然后B获得互斥量,可以继续执行了,然后让 isReady变为2,再次唤醒所有线程。。。。

运行结果:

示例2:让两个线程交替打印奇数和偶数

std::condition_variable cv;
std::mutex mtx;
int number = 1;

void Print_A()
{
	std::unique_lock<std::mutex> lock(mtx);
	while (number <= 20)
	{
		while (number % 2 == 0)
		{
			cv.wait(lock);
		}
		if (number <= 20)
		{
			cout << "A thread: " << number << endl;
		}
		number += 1;
		cv.notify_one();
	}
}

void Print_B()
{
	std::unique_lock<std::mutex> lock(mtx);
	while (number <= 20)
	{
		while (number % 2 == 1)
		{
			cv.wait(lock);
		}
		if (number <= 20)
		{
			cout << "B thread: " << number << endl;
		}
		number += 1;
		cv.notify_one();
	}
}
int main(void)
{
	std::thread tha(Print_A);
	std::thread thb(Print_B);
	tha.join();
	thb.join();
	return 0;
}

也可以利用Lambda表达式来写:

void Print_A()
{
	std::unique_lock<std::mutex> lock(mtx);
	while (number <= 20)
	{
		/*while (number % 2 == 0)
		{
			cv.wait(lock);
		}*/
		cv.wait(lock, []()->bool {return number % 2 == 1; });
		if (number <= 20)
		{
			cout << "A thread: " << number << endl;
		}
		number += 1;
		cv.notify_one();
	}
}

void Print_B()
{
	std::unique_lock<std::mutex> lock(mtx);
	while (number <= 20)
	{
		/*while (number % 2 == 1)
		{
			cv.wait(lock);
		}*/
		cv.wait(lock, []()->bool {return number % 2 == 0; });
		if (number <= 20)
		{
			cout << "B thread: " << number << endl;
		}
		number += 1;
		cv.notify_one();
	}
}

欢迎分享,转载请注明来源:内存溢出

原文地址:https://54852.com/langs/920826.html

(0)
打赏 微信扫一扫微信扫一扫 支付宝扫一扫支付宝扫一扫
上一篇 2022-05-16
下一篇2022-05-16

发表评论

登录后才能评论

评论列表(0条)

    保存