方式一:
#include
#include
#include
using namespace std;
// 自己创建的线程需要从一个函数开始允许
void myThread()
{
cout << "myThread执行完成" << endl;
}
int main()
{
// myThread可调用对象
//创建线程,开始执行
thread myObj(myThread);
//阻塞主线程,等待子线程执行完毕,
myObj.join();
// 线程分离,主线程不等待子线程完毕
//myObj.detach();
// joinable:判断是否可以调用join或detath,true(可以),false(不可以)
return 0;
}
方式二:
class TA
{
public:
TA()
{
cout << "构造函数" << endl;
}
~TA()
{
cout << "~析构函数" << endl;
}
TA(const TA& tmp)
{
cout << "拷贝构造函数" << endl;
}
void operator()() // 可调用对象
{
// 线程入口点
cout << "TA执行完成" << endl;
}
};
TA ta;
thread myObj(ta);
myObj.join();
方式三:
auto my = [] {
cout << "lambda表达式创建线程" << endl;
};
thread myObj(my);
myObj.join();
需要传入的是一个可调用对象,总的来说,可调用对象可以是以下几种情况:
普通函数
函数指针
仿函数,即重载了operator()运算符的类对象
匿名函数,即Lambda表达式
std::function
1.普通函数
void cmp()
{
}
2.函数指针
// 定义一个无参返回值是void的函数指针
typedef void (*CMPFUN)();
// using 写法定义函数指针
using CMPFUN = void(*)();
CMPFUN p1 = cmp;
3.仿函数(函数后const可加可不加,加了表示该函数不可以修改类属性)
class A{
void operator()() const{
return;
}
};
4、Lambda表达式
auto my = [] {
cout << "lambda表达式创建线程" << endl;
};
5. std::function
std::function在C++11后加入标准,可以用它来描述C++中所有可调用实体,它是是可调用对象的包装器,声明如下
#include
// 声明一个返回值为int,参数为两个int的可调用对象类型
std::function<int(int, int)> Func;
// 普通函数
int func_sum(int a, int b)
{
return a + b;
}
str指针和buf是同一个地址,所以detach后,主线程先退出,绝对会出现问题
void func(const int& i, char* str)
{
// 分析证明:i并不是num的引用,实际是值传递,即使主线程detach,子线程的i也是安全的
cout << i << endl;
cout << str << endl;
}
int num = 10;
int& mynim = num;
char buf[] = "hello world";
thread myObj(func, num, buf);
myObj.detach();
修改后:
通过将隐式类型转换,将char* 转换成string,但是这里隐藏有一个问题,就是可能主线程已经结束了,才去进行隐式类型转换, std::this_thread::get_id() 返回当前线程id
void func(const int& i,const string& str)
{
// 分析证明:i并不是num的引用,实际是值传递,即使主线程detach,子线程的i也是安全的
cout << i << endl;
cout << str << endl;
}
int num = 10;
int& mynim = num;
char buf[] = "hello world";
thread myObj(func, num, buf);
myObj.detach();
通过一段代码验证这个猜想(主线程已经退出,之后在完成隐式转换)
class A
{
public:
A(int n)
{
cout << "A的构造函数调用:" <<std::this_thread::get_id()<< endl;
}
~A()
{
cout << "A的析构函数调用:" << std::this_thread::get_id() << endl;
}
A(const A& tmp)
{
cout << "A的拷贝构造函数调用:" << std::this_thread::get_id() << endl;
}
};
void myfun(const A& tmp)
{
cout << "子线程执行完成:" << std::this_thread::get_id() << endl;
}
int num = 1;
thread myObj(myfun,num);
myObj.join();

通过控制台可以看出,既然是在子线程中构造的A对象。
修改完整版:
// 只需要修改传入的参数,在主线程中构造一个临时对象
int num = 1;
thread myObj(myfun,A(num));
myObj.detach();

通过上面代码可以看到,我们给线程传递参数虽然使用的是引用传递,但是其实还是值传递,如果我们想要修改传入对象的属性怎么修改?使用std::ref()函数
class A
{
public:
A(int n):m_n(n)
{
cout << "A的构造函数调用:" <<std::this_thread::get_id()<< endl;
}
~A()
{
cout << "A的析构函数调用:" << std::this_thread::get_id() << endl;
}
A(const A& tmp)
{
cout << "A的拷贝构造函数调用:" << std::this_thread::get_id() << endl;
}
int m_n;
};
void myfun(A& tmp)
{
tmp.m_n = 199;
cout << "子线程执行完成:" << std::this_thread::get_id() << endl;
}
A a(10);
thread myObj(myfun,std::ref(a));
myObj.join();
cout << a.m_n << endl;
cout << "主线程执行完成:" << std::this_thread::get_id()<<endl;
void myfun2(unique_ptr<int> p)
{
cout << *p << endl;
cout << "子线程执行完成:" << std::this_thread::get_id() << endl;
}
unique_ptr<int> up(new int(100));
thread myObj(myfun2,std::move(up));
myObj.join(); // 这里一定不能使用detach,因为要是使用可能主线程已经退出,然后up已经释放了
class A
{
public:
A(int n):m_n(n)
{
cout << "A的构造函数调用:" <<std::this_thread::get_id()<< endl;
}
~A()
{
cout << "A的析构函数调用:" << std::this_thread::get_id() << endl;
}
A(const A& tmp)
{
cout << "A的拷贝构造函数调用:" << std::this_thread::get_id() << endl;
}
void thread_work(int num)
{
cout << "num:" << num << endl;
}
int m_n;
};
A a(10);
thread myObj(&(A::thread_work), &a, 10); // 这个地方用引用,使用a
myObj.join();
#include
#include
#include
#include
using namespace std;
void myfun3(int num)
{
cout << "子线程执行完成:" << std::this_thread::get_id() <<"num:" << num << endl;
}
int main()
{
vector<thread> vThread;
for (int i = 0; i < 10; i++)
{
vThread.push_back(thread(myfun3, i));
}
for (auto iter = vThread.begin(); iter != vThread.end(); iter++)
{
iter->join();
}
cout << "主线程执行结束" << endl;
return 0;
}
有读有写时,对共享数据不做处理会出现异常情况
class Queue
{
public:
void inMsgQueue()
{
for (int i = 0; i < 100000; i++)
{
cout << "插入数字:" << i <<" " << std::this_thread::get_id() << endl;
m_lQueue.push_back(i);
}
}
void outMsgQueue()
{
for (int i = 0; i < 100000; i++)
{
if (!m_lQueue.empty())
{
cout << m_lQueue.front()<<endl;
m_lQueue.pop_front();
}
else
{
cout << "队列中没有数据" << endl;
}
}
}
private:
list<int> m_lQueue;
};
Queue que;
thread inMsg(&Queue::inMsgQueue, &que); // 如果不传入引用,会调用拷贝构造函数,操作的是临时对象
thread outMsg(&Queue::outMsgQueue, &que);
inMsg.join();
outMsg.join();
保护共享数据,操作时,某个线程通过代码进行加锁然后操作数据,解锁
其他线程想操作数据必须等待解锁,然后同样加锁,操作数据,解锁
互斥量(mutex),是一个类对象,也可以理解为一把锁,多个尝试用lock成员函数来加锁,只有一个线程会成功
class Queue
{
public:
void inMsgQueue()
{
for (int i = 0; i < 100000; i++)
{
cout << "插入数字:" << i <<" " << std::this_thread::get_id() << endl;
m_mutex.lock();
m_lQueue.push_back(i);
m_mutex.unlock();
}
}
void outMsgQueue()
{
for (int i = 0; i < 100000; i++)
{
m_mutex.lock();
if (!m_lQueue.empty())
{
cout <<"输出:" << m_lQueue.front() << endl;
m_lQueue.pop_front();
}
else
{
cout << "空" << endl;
}
m_mutex.unlock();
}
}
private:
list<int> m_lQueue;
mutex m_mutex; // 创建一个互斥量
};
int main()
{
Queue que;
thread outMsg(&Queue::outMsgQueue, &que);
thread inMsg(&Queue::inMsgQueue, &que);
inMsg.join();
outMsg.join();
cout << "主线程执行结束" << endl;
return 0;
}
std::lock_guard替代lock和unlock
// lock_guard在构造函数中执行了lock,在析构函数中执行了unlock,所以可以替代
std::lock_guard<mutex> sbguard(m_mutex);
比如我有两把锁,(死锁的问题,至少需要两把锁也就是两个互斥量才会产生)
锁1、锁2
线程1执行的时候,先锁1,然后去锁2。。。
线程2执行的时候,先锁2,然后锁1
此时,死锁产生
std::lock(my_mutex1,my_mutex2); // 相当于每个互斥量都调用.lock
std::lock_guard<mutex> sb1(my_mutex1,std::adopt_lock);
std::lock_guard<mutex> sb1(my_mutex2,std::adopt_lock); // 表示已经lock这里不会lock了,结束时调用unlock
unique_lock取代lock_guard,unique_lock是一个类模板,比lock_guard更方便,但是效率要低一些
std::unique_lock<mutex> unilock(m_mutex);
unique_lock第二个参数
1、std::adopt_lock // 表示互斥量已经lock了,不需要在构造函数中lock了
2、std::try_to_lock // 去锁定这个,但如果没有锁定成功,则立即返回,并不会阻塞,使用之前不能lock
// 因为他会去尝试lock
3、std::defer_lock // 不能自己先lock,否则报异常,初始化一个没有加锁的mutex,
// 可以通过owns_lock()来进行判断是否拿锁成功
if(unilock.owns_lock())
{
cout<<"成功"<<endl;
}
else
{
cout<<"不成功"<<endl;
}
unique_lock的成员函数
// 创建了一个没有加锁的mutex
std::unique_lock<mutex> unilock(m_mutex,std::defer_lock);
unilock.lock(); // 调用了unique_lock的成员函数
unilock.unlock();
unilock.try_lock(); // 不阻塞,尝试加锁,如果拿不到锁返回false,否则返回true
unilock.release(); // 返回锁管理的mutex对象指针,并释放所有权,unique_mutex不在和他有关系
// 如果原来mutex处于加锁,调用release之后,要负责解锁
std::mutex* ptex = unilock.release(); // ptex指向m_mutex
ptex->unlock();
线程休息
std::chrono::milliseconds dura(2000); // 1秒=1000毫秒,
std::this_thread::sleep_for(dura); // 休息一定时间
单例设计模式,使用频率高,
单例:整个项目中,有某个或者某些特殊的类,属于该类对象,我只能创建1个,多了创建不了
单例写法
std::mutex mymutex;
class A
{
private:
A() // 私有化构造函数
{
}
private:
static A* m_instance; // 静态成员变量
public:
static A* GetInstance()
{
if(m_instance==NULL)
{
m_instance = new A();
static Ahuishou ahui1; // 静态变量在程序退出时会释放,所以会调用析构函数
}
return m_instance;
}
// 类中套类,用来回收释放对象
class Ahuishou
{
public:
~Ahuishou()
{
if(A::m_instance!=NULL)
{
delete A::m_instance;
A::m_instance = NULL;
}
}
}
};
// 静态成员初始化
A* A::m_instance = NULL;
// 使用
A* a = A::GetInstance(); // 创建一个对象
拓展:如果使用线程创建对象,就需要加锁
std::mutex mymutex;
class A
{
private:
A() // 私有化构造函数
{
}
private:
static A* m_instance; // 静态成员变量
public:
static A* GetInstance()
{
if(m_instance==NULL)
{
unique_mutex<mutex> myunique(mymutex); //自动加锁解锁
if(m_instance==NULL)
{
m_instance = new A();
static Ahuishou ahui1; // 静态变量在程序退出时会释放,所以会调用析构函数
}
}
return m_instance;
}
// 类中套类,用来回收释放对象
class Ahuishou
{
public:
~Ahuishou()
{
if(A::m_instance!=NULL)
{
delete A::m_instance;
A::m_instance = NULL;
}
}
}
};
// 静态成员初始化
A* A::m_instance = NULL;
// 使用
//A* a = A::GetInstance(); // 创建一个对象
void myfun()
{
A* a = A::GetInstance(); // 创建一个对象
}
thread mythread(myfun);
std::call_once() c++11引入函数,该函数的第二个参数是一个函数名,能够保证函数之被调用一次,具备互斥量能力,比互斥量销毁资源更少,是通过once_flag来控制的
std::once_flag g_flag;
std::call_once(g_flag,a()); // 两个线程同时执行到这个地方,第二个线程就需要等待,后面不会调用a函数
std::condition_variable实际上是一个类,是一个和条件相关的一个类,说白了就是等待一个条件达成,这个类是需要和互斥量配合工作的,需要生产类对象
std::condition_variable m_cond; // 生成一个条件对象
std::unique_lock<std::mutex> sbguard1(m_mutex);
// wait用来等待一个东西
// 如果第二个参数lambda表达式,返回true,那么wait直接返回
// 第二个参数lambda表达式,返回值是false,那么wait将解锁互斥量,堵塞到本行
// 堵塞到,其他线程调用notify_one(),成员函数为止
// 如果wait没有第二个参数,那么就跟第二个参数lambda表达式返回false一样
// wait被唤醒只会,就会不断请求互斥量锁,如果获取不到,一直获取,如果获取到,继续走
// 如果wait有第二个表达式,wait继续重复上流程
m_cond.wait(sbguard1,[this]{ // 一个lambda表达式,可调用对象
if(!m_queque.empty()) // 队列为空反回false
return true;
return false;
});
m_cond.notify(); // 唤醒wait线程
notify_all()
通知所有线程
std::async是一个函数模板,用来启动一个异步任务,启动起来一个异步任务之后,他会返回std::future对象,std::future含有线程入口函数返回的结果,future对象的成员函数get来获取结果。
std::launch类型(枚举类型),来达到一些特殊目的,std::launch::deferred表示线程入口函数调用被延迟到future对象的wait或者get函数调用时才执行(要是没有调用wait或者和get就不会执行线程,实际上没创建),std::launch::deferred延迟调用,并没有创建新线程,是在主线程中调用的线程入口函数。
#include
int mythread()
{
cout << "start threadid:" << std::this_thread::get_id() << endl;
std::chrono::milliseconds draw(5000);
std::this_thread::sleep_for(draw); // 线程休息5000
cout << "end threadid:" <<std::this_thread::get_id()<< endl;
return 5;
}
int main()
{
cout << "主线程:" << std::this_thread::get_id() << endl;
// std::future result = std::async(std::launch::deferred,mythread); // 加入参数
std::future<int> result = std::async(mythread);
int i = 11;
i = 2;
cout << "111111111" << endl;
cout << result.get()<<endl;
cout << "主线程执行结束" << endl;
return 0;
}
是类模板,他的模板参数是,各种可调用对象,通过std::packaged_task来把各种可调用对象包装起来,方便将来将来作为线程入口函数调用,
int mythread(int n)
{
cout << "start threadid:" << std::this_thread::get_id() << endl;
std::chrono::milliseconds draw(5000);
std::this_thread::sleep_for(draw); // 线程休息5000
cout << "end threadid:" <<std::this_thread::get_id()<< endl;
return 5;
}
int main()
{
cout << "主线程:" << std::this_thread::get_id() << endl;
std::packaged_task<int(int)> mypt(mythread); // 返回值int,一个int参数
// 使用std::ref可以在模板传参的时候传入引用
std::thread t1(std::ref(mypt),1); // 线程直接开始执行
t1.join();
std::future<int> result = mypt.get_future();
cout<<result.get()<<endl;
cout << "主线程执行结束" << endl;
return 0;
}
我们能够在某个线程中给它赋值,然后我们可以再其他线程中,把这个值取出来用
void mythread1(std::promise<int>& tmp, int calc)
{
calc++;
calc *= 3;
std::chrono::milliseconds draw(5000);
std::this_thread::sleep_for(draw); // 线程休息5000
int result = calc;
tmp.set_value(result); // 结果我保存到tmp对象中
}
int main()
{
cout << "主线程:" << std::this_thread::get_id() << endl;
std::promise<int> myprom; // 声明一个promise对象,保存的值是int类型
std::thread t1(mythread1, std::ref(myprom), 180);
t1.join();
std::future<int> result = myprom.get_future();
cout << result.get();
cout << "主线程执行结束" << endl;
return 0;
}
// 是一个枚举类型
std::future<int> result = std::async(mythread);
// 等待1秒钟,希望你返回,如果没有返回,就表示超时
std::future_status status = result.wait_for(std::chrono::seconds(1)); // 等待1秒
if(status ==std::future_status::timeout)
{
// 超时,表示线程还没执行完,
}
else if(status ==std::future_status::ready)
{
// 表示线程成功返回
}
else if(status ==std::future_status::deferred)
{
// 如果async第一个参数设置为,std::launch::deferred,则条件成立
}
为什么第二次调用get时,会出现异常,是因为get的设计是一个移动语句,将里面的值移动出来,所以第二次调用里面没有东西了。
std::shared_future的get函数是复制数据
原子操作概念:可以理解为,无锁技术的并发,不需要互斥锁(加锁解锁),原子操作是多线程中,不会被打断的,程序执行片段。互斥量可以加锁代码段,原子操作一般操作一个变量的,原子操作指的是不可分割的操作,也就是说这种操作要么是完整的要么是不完整的,不可能出现半完成。
std::atomic<int> g_mycount; // 封装一个类型为int对象值
void mythreadfun()
{
for (int i = 0; i < 1000000; i++)
{
g_mycount++;
}
}
int main()
{
thread my1(mythreadfun);
thread my2(mythreadfun);
my1.join();
my2.join();
cout << g_mycount << endl;
return 0;
}
std::thread系统资源紧张,创建线程会失败,std::async可能创建也可能不创建线程,很方便获取线程返回值