线程是如何启动的呢

举报
无敌清风蓝 发表于 2023/11/22 20:10:10 2023/11/22
【摘要】 主线程是通过main启动的,那么自己创建的线程也要通过一个函数来启动,就像主线程执行完main主函数后会退出,自己创建的线程的这个函数在执行完毕后,自己创建的线程也就运行结束了一般来说,整个进程(程序)是否执行完毕的标志是主线程是否执行完,如果主线程执行完,子线程还没,那就会强行终止子线程,所以为了保持子线程一直处于运行状态,必须让主线程一直保持运行,千万不要让主线程执行完毕(后面讲到det...

主线程是通过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 函数。然后,我们调用 tdetach 函数将其与主线程分离。这样,新线程就可以在后台执行下载任务,而主线程则可以继续运行。

针对一个线程,一旦调用了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;
【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。