语言级别的多线程,优点是跨平台
底层仍然是调用系统API(识别不同系统,调用不同的系统调用)
创建线程
头文件thread
thread的构造函数:
thread() noexcept //构造线程对象,不执行任何任务
thread(thread&& _Other)noexcept//移动构造,将一个线程对象的资源所有权转移给新创建的线程对象,原来的线程对象不再代表线程
template<class Function,class... Args>
explicit thread(Function&& fx,Args&&... args)//创建线程对象,在线程中执行fx中的代码,args是传递给fx的参数,fx可以是普通函数,类的非静态、静态成员函数,lambda,仿函数
thread(const thread&) = delete;//不允许线程对象之间的拷贝
//赋值函数:删除了参数为左值的赋值函数,如果参数为右值则转移资源所有权
thread& operator= (thread&& _Other)noexcept
thread& operator= (const thread&&) = delete;
// cppRL.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
#include <iostream>
#include<thread>
#include<Windows.h>
#include<string>
using namespace std;
void func(int value,const string& str) {
for (int i = 1; i <= 10; i++) {
cout << "第" << i << "秒 " << str << value<<endl;
Sleep(1000);
}
}
class likefunc {
public:
void operator()(int value, const string& str) {
for (int i = 1; i <= 10; i++) {
cout << "第" << i << "秒 " << str << value << endl;
Sleep(1000);
}
}
};
class TestStatic {
public:
static void func(int value, const string& str) {
for (int i = 1; i <= 10; i++) {
cout << "第" << i << "秒 " << str << value << endl;
Sleep(1000);
}
}
void func2(int value, const string& str) {
for (int i = 1; i <= 10; i++) {
cout << "第" << i << "秒 " << str << value << endl;
Sleep(1000);
}
}
};
int main()
{
thread t1(func, 5, "cppThread");
cout << "thrad t1 end" << endl;
//lambda
auto f2 = [](int a) { cout << "lambda " <<a<< endl; };
thread t2(f2,2);
//仿函数
thread t3(likefunc(),3,"likeThread");
//类的静态和非静态函数
thread t4(TestStatic::func,3,"static");
//用类的普通成员函数要保证对象的生命周期比子线程长
TestStatic normal;
thread t5(&TestStatic::func2,&normal,3,"normal");
thread t5();
t1.join();
t2.join();
t3.join();
t4.join();
}
线程资源的回收
子线程资源不回收会产生僵尸线程
线程有异步性,线程的任务函数返回则线程终止,主线程退出则全部子线程终止(linux上),但是在语言级别会报错
//主线程先退出
#include <iostream>
#include<thread>
#include<Windows.h>
#include<string>
using namespace std;
void func(int value,const string& str) {
for (int i = 1; i <= 10; i++) {
cout << "第" << i << "秒 " << str << value<<endl;
Sleep(1000);
}
}
int main()
{
thread t1(func, 5, "cppThread");
cout << "thrad t1 end" << endl;
return 0;
t1.join();
}
vs中的表示有些误导,在Linux下更直观
线程不共享的:栈、寄存器、状态、程序计数器
线程间共享的有:堆,全局变量,静态变量;
进程占有的资源有:地址空间,全局变量,打开的文件,子进程,信号量、账户信息
join()
等待子线程退出(产生阻塞),回收资源
detach()
分离子线程, 子线程退出,系统自动回收资源,分离后不可以join,主线程结束,整个进程结束,所有子线程自动结束
joinable()
判断子线程的分离状态,返回bool
void func(int value,const string& str) {
for (int i = 1; i <= 10; i++) {
cout << "第" << i << "秒 " << str << value<<endl;
Sleep(1000);
}
}
int main()
{
thread t1(func, 5, "cppThread");
t1.detach();
cout << t1.joinable() << endl;
cout << "thread t1 end" << endl;
Sleep(12000);
}
子线程如何结束:子线程函数运行完成则线程结束
当前线程
命名空间this_thread
下的
//get_id();获取线程ID,thread类有同名函数
void func(int value,const string& str) {
cout << "t1 id:" << this_thread::get_id() << endl;
for (int i = 1; i <= 10; i++) {
cout << "第" << i << "秒 " << str << value<<endl;
//Sleep(1000);
this_thread::sleep_for(1s);
this_thread::sleep_for(chrono::seconds(1));
}
}
int main()
{
thread t1(func, 5, "cppThread");
cout << t1.get_id();
t1.join();
cout << "thread t1 end" << endl;
cout << "main thread id:" << this_thread::get_id() << endl;
}
//sleep_for();
//Sleep(1000);
this_thread::sleep_for(1s);
this_thread::sleep_for(chrono::seconds(1));
//sleep_until(); 休眠到指定时间点(定时任务)
sleep_until(chrono::system_clock::now() + 1s);
//yield();使线程主动让出cpu时间片
//thread::swap()交换两个线程对象
int main()
{
thread t1(func, 5, "cppThread");
thread t2(func, 5, "cppThread2");
cout <<"t1" << t1.get_id() << endl;
cout << "t2" << t2.get_id() << endl;
t1.swap(t2);
t1.join();
t2.join();
cout << "t1" << t1.get_id() << endl;
cout << "t2" << t2.get_id() << endl;
cout << "thread t1 end" << endl;
cout << "main thread id:" << this_thread::get_id() << endl;
}
//拷贝构造和移动
thread t3 = t2;//报错
thread t3 = move(t2);//t2转义为右值,使用移动构造函数,t2转移资源后不再代表线程,则要
t3.join();
//hardware_concurrency()获取硬件支持的并发线程数,正常返回支持的并发线程数,若值非良定义或不可计算,则返回0
this_thread::yield();//线程让出当前的cpu时间片,等待下一次调度
mutex互斥锁
以下模拟三个窗口卖100张票程序,存在竞态条件,每次结果随着cpu对线程的不同调用顺序,可能不同
int cnt = 100;
void sell(int i)
{
while (cnt > 0)
{
cout << "窗口:" << i << "卖出第:" << cnt << "张票" << endl;
cnt--;
this_thread::sleep_for(chrono::milliseconds(100));
}
}
int main()
{
list<thread>threads;
for (int i = 1; i <= 3; ++i)
{
threads.push_back(thread(sell,i));
}
for (thread& t : threads)
{
t.join();
}
cout << "所有窗口结束售卖" << endl;
}
//没有对临界资源互斥访问
void sell(int i)
{
while (cnt > 0)
{
//仍然有问题,cnt==1时,多个线程可能在一段时间内都会进入while循环,
mtx.lock();
cout << "窗口:" << i << "卖出第:" << cnt << "张票" << endl;
cnt--;
mtx.unlock();
this_thread::sleep_for(chrono::milliseconds(100));
}
}
//改进:
while (cnt > 0)
{
mtx.lock();
if (cnt > 0)
{
cout << "窗口:" << i << "卖出第:" << cnt << "张票" << endl;
cnt--;
}
mtx.unlock();
this_thread::sleep_for(chrono::milliseconds(100));
}
lock_guard,unique_lock
如果在mtx.unlock前,异常,导致锁没有被释放:
使用lock_guard,封装互斥锁,且使用时加{}作用域,出作用域则自动进行析构,释放锁,类似scoped_ptr,不允许拷贝构造和赋值
void sell(int i)
{
while (cnt > 0)
{
{ //加作用域
lock_guard<mutex>lock(mtx);
if (cnt > 0)
{
cout << "窗口:" << i << "卖出第:" << cnt << "张票" << endl;
cnt--;
}
}
this_thread::sleep_for(chrono::milliseconds(100));
}
}
unique_lock类似unique_ptr,允许右值引用的拷贝构造和赋值
unique_lock<mutex>lock(mtx);
if (cnt > 0)
{
cout << "窗口:" << i << "卖出第:" << cnt << "张票" << endl;
cnt--;
}
lock.unlock();
保证所有线程都能释放锁,防止死锁发生
lock_guard不可能用于函数参数传递或返回过程中,因为lock_guard不能拷贝构造和赋值,只能用于临界区代码段的简单互斥操作中
unique_lock可以用于函数调用过程中(右值引用)
atomic 原子类型
基于CAS(exchange,swap)操作(无锁操作)
对于类似全局变量的++,--操作,互斥锁较为繁琐
atomic_bool isReady = false;
atomic_int cnt = 0;
void task()
{
while (!isReady)
{
this_thread::yield();
}
for (int i = 0; i < 100; ++i)
{
cnt++;
}
}
int main()
{
list<thread>threads;
for (int i = 1; i <= 10; ++i)
{
threads.push_back(thread(task));
}
this_thread::sleep_for(chrono::seconds(3));
isReady = true;
for (thread& t : threads)
{
t.join();
}
cout << "count:" << cnt << endl;
}
多线程会对共享变量缓存,所以主线程改变isReady后,子线程不一定能立刻看到改后的值
所以要防止缓存:访问原始内存中的值
volatile atomic_bool isReady = false;
volatile atomic_int cnt = 0;
线程间的同步通信
生产者消费者模型
共享一个队列
C++ STL中的所有容器都不是线程安全的
#include <iostream>
#include<thread>
#include<list>
#include<mutex>
#include<atomic>
#include<queue>
#include<condition_variable>
using namespace std;
mutex mtx;
condition_variable cv;
//不为空则消费,为空则通知生产,为满则通知消费
//生产者生产一个,通知消费者消费一个,消费完,通知生产者生产
class Queue
{
public:
void put(int val) //生产
{
//生产者消费者同步
unique_lock<mutex>lck(mtx); //对临界区互斥
while (!que.empty())
{
//que不为空,通知消费,生产者线程进入等待状态,且要释放互斥锁(使用条件变量)
cv.wait(lck);//进入等待状态则会将锁释放
}
que.push(val);
cv.notify_all();//通知其他所有线程,消费,其他线程得到通知时,从等待->阻塞->获取到互斥锁才能继续执行
cout << "生产者生产:" << val << endl;
}
int get() //消费
{
unique_lock<mutex>lck(mtx);
while (que.empty())
{
cv.wait(lck);//进入等待状态则会将锁释放
}
int val = que.front();
que.pop();
cv.notify_all();//通知生产者生产
cout << "消费者消费:" << val << endl;
return val;
}
private:
queue<int>que;
};
void producer(Queue* que)
{
for (int i = 0; i < 10; i++)
{
que->put(i);
this_thread::sleep_for(chrono::microseconds(100));
}
}
void consumer(Queue* que)
{
for (int i = 0; i < 10; i++)
{
que->get();
this_thread::sleep_for(chrono::microseconds(100));
}
}
int main()
{
Queue que;
thread t1(producer,&que);
thread t2(consumer,&que);
t1.join();
t2.join();
}
条件变量wait使线程进入等待状态,且可以把持有的互斥锁先释放掉
notify_all()
通知在条件变量上等待的线程,条件成立,解除等待
其他在条件变量上等待的线程,从等待->阻塞->获取到互斥锁才能继续执行
标签:多线程,thread,int,void,C++,线程,que,include From: https://www.cnblogs.com/ziggystardust-pop/p/17110395.html