前言
说来惭愧,写了很久的代码,一个单线程通全部。可能是接触的项目少吧,很多多线程的概念其实都知道,但是实战并没有用上。前段时间给公司软件做一个进度条,涉及到多线程的操作,主要是不影响主线程UI卡死的问题。
这篇博客写一下多线程的一个demo
1. 多线程创建
创建多线程在C++11的标准下使用的是std::thread。
std::thread后面的参数有下面几种:
函数指针或可调用对象(Callable Object)
:这是新线程要执行的函数或函数对象。可以是函数指针、函数对象、lambda 表达式等。
传递给函数的参数
:如果需要向线程函数传递参数,这些参数应该在创建线程时一并提供。
其他可选参数
:一些可选参数可以通过第二个参数传递给std::thread构造函数,例如线程的启动参数等。
比如我下面这句话std::thread m_thOne = std::thread(&fun1);
创建一个名为m_thOne的线程,线程调用的是函数f1。这里第一个参数要传递函数的地址。
如果有一个函数void fun3(int a,string b);这个函数是类FUN的一个成员函数。
class FUN
{
public::
void fun3(int a,string b){};
}
那么在main中我想开一个线程跑这个fun3函数,现有一个FUN的对象指针m_pfun
,一个int类型参数a1,string类型参数b1。
在开线程的时候传参的写法如下:
std::thread(&fun3,m_pfun,a1,b1);
再结合一下上面的说明,第一个参数是函数的地址,第二个参数是谁调用这个函数,对应的这个调用函数的对象地址,第三个以及后面的参数是调用函数的参数。
2. 多线程释放
我们都知道,在使用new的方式创建新的对象,这个对象使用完之后如果不用delete进行释放,那么就会造成内存泄露。
我们创建线程之后,释放通常由 std::thread 的析构函数自动处理,但是关于这一点,存在一个注意点
。
那如果子线程在运行,但主线程已经跑完了,此时要关闭程序,就会调用析构,可是子线程还在运行,这就会出发异常。
正常的程序应该是,等所有的线程都运行完之后,要确保每个线程在主线程退出之前完成执行,就需要提及到函数join()
。
join() 用于阻塞当前线程,直到被调用的线程完成执行。调用 join() 后,当前线程会等待直到被 join() 的线程终止,然后继续执行。
确保在线程结束后主线程能够正确处理资源,防止线程终止时资源泄漏。
与之相类似的还有一个函数detach()
detach() 用于将线程与其主线程分离。分离后的线程在后台独立运行,主线程不再需要等待它完成。
子线程在分离后继续运行,即使主线程已经结束。分离线程的资源由系统自动管理。
归纳一下:
join()会阻塞线程,让主线程等join的子线程执行完再接着执行;(插队后排队)
detach()不会阻塞主线程,主线程执行结束也和子线程无关。(从原队中踢出后开新的一队)
所以要确保在线程结束后主线程能够正确处理资源,防止线程终止时资源泄漏,最好的方式其实使用join()函数。
3. 实例
接下来我们看一个实例。
这里我把刚才提到的FUN类放进例子中,通过一个FUN的一个指针m_pfun进行调用fun3函数,最后进行了释放。
这里delete是把指针指向的内存释放,但是指针还是指向那个内存,所以指针还要置空。
fun1和fun2函数和fun3基本类似,少了两个参数,也是延时输出。
我例子中都用了,下面这种方式来确保程序的正常结束。
if (m_thOne.joinable())
{
m_thOne.join();
}
看明白下面的实例,多线程的就可以尝试写一写了。
#include <iostream>
#include <thread>
#include <chrono>
class FUN
{
public:
void fun3(int a, std::string b)
{
std::cout << "fun3 start" << std::endl;
std::chrono::seconds delay(7);
std::this_thread::sleep_for(delay);
std::cout << "fun3 end" << std::endl;
};
};
void fun1()
{
std::cout << "fun1 start" << std::endl;
std::chrono::seconds delay(3);
std::this_thread::sleep_for(delay);
std::cout << "fun1 end" << std::endl;
}
void fun2()
{
std::cout << "fun2 start" << std::endl;
std::chrono::seconds delay(5);
std::this_thread::sleep_for(delay);
std::cout << "fun2 end" << std::endl;
}
int main()
{
std::thread m_thOne = std::thread(&fun1);
std::thread m_thTwo = std::thread(&fun2);
FUN* m_pfun = new FUN();
int a =1;
std::string b = "b";
std::thread m_thThree = std::thread(&FUN::fun3, m_pfun, a, b);
std::cout << "Hello World!\n";
if (m_thOne.joinable())
{
m_thOne.join();
}
if (m_thTwo.joinable())
{
m_thTwo.join();
}
if (m_thThree.joinable())
{
m_thThree.join();
}
delete m_pfun;
m_pfun = nullptr;
return 0;
}
总结
多线程还是很有用的,C++程序员进阶的必通之路。大学里面学的那些小玩意儿,单线程就能搞定所有,真正的大型项目,还是会有很多多线程相关的。
记住这个多线程demo的简单使用,那么后面进一步拓展就手到擒来了。