C++-std::thread线程
一、thread
本着能用STL就不用其他库的原则,本文以c++11的std::thread作为接口开发。【VS2010不支持C++11的线程,VS2013支持】
根据我另一个帖子,线程函数只能是全局或者静态的。https://www.cnblogs.com/judes/p/5921104.html
全局函数:只能访问全局变量,需要注意加互斥锁
静态函数:只能访问静态变量
项目中常常需要线程访问类中的成员,本文也重点实现这个功能,下面提供两种方式。使用多线程卖票举例:
卖票头文件:
#pragma once #include <thread> #include <mutex> #include <Windows.h> using namespace std; class SellTickts { public: void SellTickts::seller_1(); void SellTickts::seller_2(); int ticketNum = 10; mutex Mutex; public: SellTickts(); virtual ~SellTickts(); void sell(); friend void sell(void* param); };
卖票源文件:
#include "stdafx.h" #include "SellTickts.h" SellTickts::SellTickts() { } SellTickts::~SellTickts() { } //函数名称:售票员1 //函数作用:NULL //函数参数:NULL //函数返回值:NULL //函数信息:NULL //备注:NULL void SellTickts::seller_1() { for (;;) { Mutex.lock(); if (ticketNum == 0) { break; } else { ticketNum--; printf("Seller_1 sold a ticket, remains %d.\n", ticketNum); if (ticketNum == 0) { printf("Tickets has been sold out.\n"); break; } } Mutex.unlock();
Sleep(100); } } //函数名称:售票员2 //函数作用:NULL //函数参数:NULL //函数返回值:NULL //函数信息:NULL //备注:NULL void SellTickts::seller_2() { for (;;) { Mutex.lock(); if (ticketNum == 0) { break; } else { ticketNum--; printf("Seller_2 sold a ticket, remains %d.\n", ticketNum); if (ticketNum == 0) { printf("Tickets has been sold out.\n"); break; } } Mutex.unlock();
Sleep(100); } } //函数名称:类函数售票 //函数作用:NULL //函数参数:NULL //函数返回值:NULL //函数信息:NULL //备注:NULL void SellTickts::sell() { printf("class sell.\n"); thread thread1(&SellTickts::seller_1,this); thread thread2(&SellTickts::seller_2,this); thread1.detach(); thread2.detach(); } //函数名称:友元函数售票 //函数作用:NULL //函数参数:NULL //函数返回值:NULL //函数信息:NULL //备注:NULL void sell(void* param) { printf("friend sell.\n"); SellTickts* pThis = (SellTickts*)param; thread thread1(&SellTickts::seller_1, pThis); thread thread2(&SellTickts::seller_2, pThis); thread1.detach(); thread2.detach(); }
main函数:
// ControlTest.cpp : 定义控制台应用程序的入口点。 // void test(); #include "stdafx.h" #include "SellTickts.h" int _tmain(int argc, _TCHAR* argv[]) { SellTickts sellTickets; sellTickets.sell();//类函数作为函数入口
sell(&sellTickets);//友元函数作为函数入口
thread t(test);//全局函数作为函数入口
t.detach();
thread t1(&SellTickets::seller_1,this);//类外部使用类函数作为函数入口
t1.detach();
) while (true) { } return 0; }
关于友元函数的分析:https://www.cnblogs.com/judes/p/8202602.html
分析:
1、类函数作为线程入口:thread thread1(&SellTickts::seller_1,this);
第一个参数是函数的地址,这里有个概念:无论类都有多少个对象,这些对象所使用的类函数都是同一个地址,只有属性地址不同。
class A{ int temp; void fun(); };
无论A类有多少个对象,这些对象所调用fun函数时,都是用的同一个地址,这个地址在类A定义的时候就有了;但是每个对象中的属性test地址都是不同的。那么fun函数如何区分当前是被哪个对象调用的呢?fun函数有一个默认参数this,当对象声明时,就有了一个对应的this,谁调用fun,就把谁的this传进去,这样就没问题了。
注意:如果想要类函数的地址,需要&类::函数,不能&对象::函数,这也是我刚开始不理解的地方。
第二个参数时this,是因为类函数都有默认参数this,所以这里必须传递参数this进去,否则编译出错。
2、友元函数作为线程入口:thread thread1(&SellTickts::seller_1, pThis);
其实原理一样,不同的就是友元函数灭有默认this参数,需要程序员手动放入类指针。
3、使用全局函数作为线程入口:thread t(test);
不知道为啥这里不使用函数的地址
ps:怎么获取线程id和程序id
直接使用thread的get_id函数返回的是个thread类的内部类,不方便获取id,可以使用各平台自己的api
#ifndef __linux__ #include "windows.h" #else #include "unistd.h" #endif #ifndef __linux__ printf("now pid is %d \n", GetCurrentProcessId()); printf("now tid is %d \n", GetCurrentThreadId()); #else printf("now pid is %d \n", getpid()); printf("now tid is %d \n", gettid()); #endif
ps:关于多线程调用同一全局函数的理解【转:https://www.cnblogs.com/liangjf/p/9801496.html】
每个线程有自己的堆栈空间,而全局函数是只读的,当多个线程使用这个函数的时候,应该会把函数功能复制一份到自己的堆栈,然后函数内部的变量都放在自己的空间内,这样就互不影响了。
当然如果访问的是同一个全局变量,加互斥锁就可以了。
ps:thread::detach和join的区别
join()
操作是在std::thread t(func)
后“某个”合适的地方调用,其作用是回收对应创建的线程的资源,避免造成资源的泄露。detach()
操作是在std::thread t(func)
后马上调用,用于把被创建的线程与做创建动作的线程分离,分离的线程变为后台线程,其后,创建的线程的“死活”就与其做创建动作的线程无关,它的资源会被init进程回收。
主要谈join:
void test() { } bool do_other_things() { } int main() { std::thread t(test); int ret = do_other_things(); if(ret == ERROR) { return -1; } t.join(); return 0; }
可能执行不到join
解决:
class mythread { private: std::thread &m_t; public: explicit mythread(std::thread &t):m_t(t){} ~mythread() { if(t.joinable()) { t.join() } } mythread(mythread const&) = delete; mythread& operate=(mythread const&) = delete; } void test() { } bool do_other_things() { } int main() { std::thread t(test); mythread q(t); if(do_other_things()) { return -1; } return 0; }
总结:
用detach,当前函数所在线程不会等待detack的子线程;
用join,当前函数所在线程会等待detack的子线程结束,再结束自己
ps:
1、休眠
#include <thread> #include <chrono> std::this_thread::sleep_for(std::chrono::milliseconds(50));
2、获取线程id
cout<<std::this_thread::get_id();
二、std::async
1、原型
template<typename _Fn, typename... _Args> future<typename result_of<_Fn(_Args...)>::type> async(launch __policy, _Fn&& __fn, _Args&&... __args); template<typename _Fn, typename... _Args> future<typename result_of<_Fn(_Args...)>::type> async(_Fn&& __fn, _Args&&... __args);
主要区别是是否有启动参数launch,定义如下:
enum class launch { async = 1, deferred = 2 };
- std::launch::async:在调用async就开始创建线程。
- std::launch::deferred:延迟加载方式创建线程。调用async时不创建线程,直到调用了future的get或者wait时才创建线程。
使用无参数的构造函数,默认启动策略是std::launch::async|std::launch::deferred,由系统决定何种测试,windows上实测是deferred。
2、启动
std::future<bool> ret = std::async(&Store::_writeTagFile, this, dirPath); std::future<bool> ret = std::async(std::launch::async, &Store::_writeTagFile, this, dirPath);
返回一个std::future,详情见:https://www.cnblogs.com/judes/p/15031437.html
PS:
1、获取线程id
cout<<std::this_thread::get_id();
2、创建线程+类函数
std::thread t(&A::a, this);
t.detach();
由于类默认有个this参数,所以传进去
3、创建线程+全局函数
void f()
{
}
std::thread t(f);
t.detach();
4、创建线程+匿名函数
std::thread t([this](){
});
5、创建线程+函数对象
std::function<void()> f;
f = [this](){
};
std::thread t(f);
t.detach();
长风破浪会有时,直挂云帆济沧海!
可通过下方链接找到博主
https://www.cnblogs.com/judes/p/10875138.html