线程是如何启动的呢
主线程是通过main启动的,那么自己创建的线程也要通过一个函数来启动,就像主线程执行完main主函数后会退出,自己创建的线程的这个函数在执行完毕后,自己创建的线程也就运行结束了
一般来说,整个进程(程序)是否执行完毕的标志是主线程是否执行完,如果主线程执行完,子线程还没,那就会强行终止子线程,所以为了保持子线程一直处于运行状态,必须让主线程一直保持运行,千万不要让主线程执行完毕(后面讲到detach时会打破这个规律)
1.写一个自己的线程
//在头文件里
#include<thread>
void myprint()
{
cout << "我的线程开始了" << endl;
//----
cout << "我的线程技术了" << endl;
return;
}
//在main中
std:thread mytobj(myprint); //这就是创建线程的代码,显然这是一个线程对象,然后给的参数是一个函数名,代表这个线程就是从myprint这个函数(初始函数)开始运行
mytobj.join(); //join会卡在这里,等mypoint线程执行完毕,程序流程才会继续往下走
cout << "main主线程执行结束" << endl; //这行由主线程执行,主线程从main返回,则整个进程执行结束
std:thread mytobj(myprint); 这里的myprint就是一个可调用对象作为thread构造函数的实参来构造这个thread对象
这行代码一执行,新线程就创建出来了,并且立即开始执行新线程的初始函数mypoint
join是加入,汇合的意思,也就是阻塞的意思,主线程等子线程执行完毕,执行流程最终汇合到一起(子线程执行完毕,执行流程回归主线程并执行完main主函数)
如果没有join的话可能上面输出的三句话就乱序了
一般来说得等子线程制造型完,主线程才能推出,不过现在有detach成员函数来打破这个规则,detach中文是分离的意思,就是主线程不和子线程汇合了,主线程执行主线程的,子线程执行子线程的,主线程不用等子线程结束,可以自己先结束执行,这并不影响子线程的执行
为什么会有它呢,因为如果创建了很多子线程,让主线程逐个等子线程结束,这种编程方法不一定是最好的,所以引入了detach这种写法
有的资料解释称,线程一且 detach 后,那么与这个线程关联的 thread 对象就会失去与这个线程的关联(那当然了,因为 thread 对象是在主线程中定义的),此时这个线程就会驻留在后台运行(主线程跟这个线程也就相当于失去联系了),这个新创建的线程相当于被C++运行时库接管了,当这个线程执行完后,由运行时库负责清理该线程相关的资源。这种分离的线程作为开发 Linux的读者可能比较熟悉它的名字-守护线程(大概守护进程更听说)。
2.detach
detach
是 C++ 中 std::thread
类的一个成员函数,它的作用是将线程对象与其所代表的线程分离,使它们能够独立运行。调用 detach
函数后,线程对象不再与其所代表的线程关联,因此我们不能再对这个线程进行任何操作。当线程结束运行时,它所占用的资源会被自动释放。
#include <iostream>
#include <thread>
#include <chrono>
void thread_function() {
std::cout << "Thread started." << std::endl;
std::this_thread::sleep_for(std::chrono::seconds(5));
std::cout << "Thread finished." << std::endl;
}
int main() {
std::thread t(thread_function);
t.detach();
std::cout << "Main thread continues to run." << std::endl;
std::this_thread::sleep_for(std::chrono::seconds(2));
std::cout << "Main thread finished." << std::endl;
return 0;
}
在这个例子中,我们定义了一个名为 thread_function
的函数,它会在新线程中运行。在 main
函数中,我们创建了一个 std::thread
对象 t
来代表这个新线程,并调用它的 detach
函数将其与线程对象分离。这样,新线程就可以独立运行,而不受主线程的影响。当主线程结束运行时,新线程仍然会继续运行,直到它自己结束。
让新线程独立运行有很多实际应用。例如,在开发一个网络应用程序时,我们可能需要在后台执行一些耗时的任务,如下载文件、处理数据等。这些任务通常会阻塞主线程,导致用户界面无响应。为了避免这种情况,我们可以将这些耗时的任务放到一个新线程中运行,并调用 detach
函数将其与主线程分离。这样,主线程就可以继续处理用户输入,而新线程则在后台执行耗时的任务。
下面是一个简单的例子,演示了如何使用 detach
函数来实现后台下载文件的功能:
#include <iostream>
#include <thread>
#include <chrono>
void download_file(const std::string& url) {
std::cout << "Starting download from " << url << std::endl;
std::this_thread::sleep_for(std::chrono::seconds(5));
std::cout << "Download finished." << std::endl;
}
int main() {
std::thread t(download_file, "http://example.com/file.zip");
t.detach();
std::cout << "Main thread continues to run." << std::endl;
std::this_thread::sleep_for(std::chrono::seconds(2));
std::cout << "Main thread finished." << std::endl;
return 0;
}
在这个例子中,我们定义了一个名为 download_file
的函数,它接受一个字符串参数表示要下载文件的 URL。在 main
函数中,我们创建了一个 std::thread
对象 t
来代表一个新线程,并调用它的构造函数来启动 download_file
函数。然后,我们调用 t
的 detach
函数将其与主线程分离。这样,新线程就可以在后台执行下载任务,而主线程则可以继续运行。
针对一个线程,一旦调用了detach,就不能再调用join了,否则程序会异常
还有一个是joinable,这是判断是否可以成功使用join或者detach
2.其他创建线程的写法
前面是用了一个函数 myprint 创建了一个线程,线程开启后直接执行 myprint 函数那么既然 thread类接受的是一个可调用对象作为参数来创建线程,那么,看一看换种写法来创建线程
2.1 用类来创建线程
创建一个名字叫作TA 的类
class TA
{
public:
//之前提到的可调用对象:重载圆括号
void operator()()
{
cout << "开始执行了" << endl;
// ---
cout << "执行结束了" << endl;
}
}
在main中
TA ta;
thread mytobj3(ta); //ta可调用对象:这里不可以是临时对象thread mytobj3(TA());否则编译无法通过
mytobj3.join();
cout << "main主函数执行结束"
但是类尽量别detach
class TA
{
public:
TA(int& i) :m_i(i){}
void operator()()
{
cout << "mi1的值为:" << m_i << endl; // 隐患,m_i可能没有有效值
cout << "mi2的值为:" << m_i << endl; // 隐患,m_i可能没有有效值
cout << "mi3的值为:" << m_i << endl; // 隐患,m_i可能没有有效值
cout << "mi4的值为:" << m_i << endl; // 隐患,m_i可能没有有效值
cout << "mi5的值为:" << m_i << endl; // 隐患,m_i可能没有有效值
cout << "mi6的值为:" << m_i << endl; // 隐患,m_i可能没有有效值
}
int& m_i;
};
int main()
{
int myi = 6;
TA ta(myi);
thread mytobj3(ta);
mytobj3.detach();
cout << "主线程执行结束" << endl;
}
m_i是一个引用,绑定是main里面的myi变量,当主线程结束的时候,子线程可能还在后台继续运行,这时候主线程结束,myi被销毁,子线程仍然用被销毁的myi会有不可预知的后果
2.2用lambda表达式来创建线程
auto mylamthread = [] {
cout << "线程开始" << endl;
cout << "线程结束" << endl;
};
thread mytobj4(mylamthread);
mytobj4.join();
cout << "主函数结束" << endl;
- 点赞
- 收藏
- 关注作者
评论(0)