等待线程结束

正常环境下等待线程结束

如果需要等待线程结束,就在线程的实例对象上调用join()。在管理线程之创建线程最后的例子中,用my_thread.join()代替my_thread.detach()就可以确保在函数终止前、局部变量析构前,线程会终止。在这种情况下,用分开的线程来运行函数就没有什么意义了。因为在等待my_thread终止时,这个线程就不做任何事情了。在实际的工程应用中,要么这个线程做自己的事,要么创建多个线程等待它们结束。

join()是简单粗暴的,或者你等待线程终止,或者你不等待。如果想要更加精确控制,例如检查这个线程是否终止,或只是等待某一段时间,这是你必须使用其他方法例如条件变量等。线程调用join()会清理相应的内存空间,调用join()后,std::thread对象就不再和这个线程相关了。这也就是意味着,对于一个线程实例对象,你仅仅可以调用join()一次,在调用后,这个线程对象就不在是joinable,调用joinable()会返回false。

异常环境下的情况

在线程对象销毁前,必须确保调用了join()或detach()。如果要分离一个线程,通常是在创建线程后立即调用detach(),因此这不是问题。但是如果要等待一个线程,那么必须小心去找到在代码的什么地方调用join()。也就是说,因为异常的发生,join()有可能被跳过而得不到执行。

为了避免应用程序在异常时终止,要考虑到在异常情况下怎么做。通常来讲,在没有异常情况下如果调用join(),那么再异常情况下也需要调用join(),这样才可以避免意外的生命周期的问题。在实际代码中,可以使用try/catch:

struct func;

void f()
{
	int some_local_state=0;
	func my_func(some_local_state);
	std::thread t(my_func);
	try
	{
		do_something_in_current_thread();
	}
	catch(...)
	{
		t.join();
		throw;
	}
	t.join();
}

上面代码使用try/catch模块来确保调用join(),无论是否发生异常。使用try/catch有点冗长(verbose),而且容易出现作用域错误,不是理想的解决方法。

另一种方法是RAII(Resource Acquisition Is Initialization资源获取即初始化),在类的析构函数中调用join()

class thread_guard
{
	std::thread& t;
public:
	explicit thread_guard(std::thread& t_):t(t_){}
	~thread_guard()
	{
		if(t.joinable())//先检查是否已经掉用过join
			t.join();
	}
	//下面是禁止使用拷贝构造函数和赋值操作符,这需要
	//C++11的支持
	thread_guard(thread_guard const&)=delete;
	thread_guard& operator=(thread_guard const&)=delete;
};
struct func;
void f()
{
	int some_local_state=0;
	func my_func(some_local_state);
	std::thread t(myfunc);
	thread_guard g(t);
	do_something_in_current_thread();
}

上面的原理是局部变量会按照构造的逆顺序销毁,因为这些变量都在栈上。thread_guard对象g首先销毁,在它的析构函数调用了join()。

上面代码中禁用了复制构造函数和赋值操作符。这是为了防止thread_guard对象超出thread对象的生命周期。

如果不需要等待线程终止,那么可以用分离来避免异常安全问题。

在后台运行线程

在线程对象调用detach()后,线程就分离了,在后台运行了,没有直接的方法了和它通信了。线程在后台运行后,控制权就交给了C++ Runtime Library,来保证当线程存在时资源的回收使用。

分离的线程叫做守护线程,这个来源于Unix中的守护进程(在后台运行,没有显示的用户接口)。这样的线程常常是长时间运行的,它们经常在应用的整个生命周期都在运行,清理不使用的对象或者优化数据结构等。另一种极端情况是,它可以使用分离的线程(如果有一种机制可以检测某个线程是否完成任务,或线程是用来即发即弃任务“fire and forget”)。

在分离一个线程前,必须确保线程可以分离,即std:thread的对象还和线程关联。和调用join()前的检查相同,先调用joinable(),如果返回true,再调用detach。

考虑一个文件处理的应用。这个应用可以同时编辑多个文档。编辑窗口是独立的,它们的代码相同,但是处理的数据不同。一种处理的方法就是在打开一个新的文档时就创建新的线程,然后分离。

void edit_document(std::string const& filename)
{
	open_document_and_display_gui(filename);
	while(!done_editing())//编辑是否完成
	{
		user_command cmd=get_user_input();//用户输入命令
		if(cmd.type==open_new_document)//要打开新文档
		{
			std::string const new_name=get_filename_from_user();
			std::thread t(edit_document,new_name);//创建线程
			t.detach();//分离,在后台运行
		}
		else
		{
			process_user_input(cmd);
		}
	}
}
时间: 2024-08-04 13:56:20

等待线程结束的相关文章

编写多线程代码时,启动线程后等待线程结束方法

在编写多线程代码时,如果主线程结束,那么子线程也会随之结束,如何等待线程结束再往下执行.   等待线程执行完成代码.   线程代码:   package demo; import java.util.concurrent.CountDownLatch; public class NodeSqlThread1 implements Runnable{         private CountDownLatch cdlSync;         public NodeSqlThread1(Coun

使用join和CountDownLatch来等待线程结束

1.join方法的实现 join只能在start()之后调用, 调用线程会等待子线程结束后再运行 public class ThreadJoinTest { public static void main(String[] args) { int count = 100; Thread[] threads = new Thread[count]; IntStream.range(0, count) .forEach(i -> { threads[i] = new Thread("threa

等待线程结束(join)

很多情况下,线程之间的协作和人与人之间的协作非常相似.一种非常常见的合作方式就是分工合作.以我们非常熟悉的软件开发为例,在一个项目进行时,总是应该有几位号称是"需求分析师"的同事,先对系统的需求和功能点进行整理和总结,然后,以书面形式给出一份需求说明或者类似的参考文档,然后,软件设计师,研发工程师才会一拥而上,进行软件开发.如果缺少需求分析师的工作输出,那么软件研发的难度可能会比较大.因此,作为一名软件研发人员,总是喜欢等待需求分析师完成他应该完成的任务后,才愿意投身工作.简单地说,就

C# 多线程join的用法,等待多个子线程结束后再执行主线程

等待多个子线程结束后再执行主线程 class MultiThread{ #region join test public void MultiThreadTest() { Thread[] ths = new Thread[2]; ths[0] = new Thread(Method1); ths[1] = new Thread(Method2); foreach (Thread item in ths) { //首先让所有线程都启动 item.Start(); //试想一下在这里加上item.

Java Thread.join()详解--父线程等待子线程结束后再结束

目录(?)[+] 阅读目录 一.使用方式. 二.为什么要用join()方法 三.join方法的作用 join 四.用实例来理解 打印结果: 打印结果: 五.从源码看join()方法 join是Thread类的一个方法,启动线程后直接调用,例如: ? 1 Thread t = new AThread(); t.start(); t.join(); 回到顶部 在很多情况下,主线程生成并起动了子线程,如果子线程里要进行大量的耗时的运算,主线程往往将于子线程之前结束,但是如果主线程处理完其他的事务后,需

等待线程的终止

在一些情形下,我们必须等待线程的终止.例如,我们的程序在执行其他的任务时,必须先初始化一些必须的资源.可以使用线程来完成这些初始化任务,等待线程终止,再执行程序的其他任务. 为了达到这个目的,我们使用Thread类的join()方法.当一个线程对象的join()方法被调用时,调用它的线程将被挂起,直到这个线程对象完成它的任务. package concurrency; import java.util.Date; import java.util.concurrent.TimeUnit; pub

JAVA笔记13__创建线程/线程休眠/等待线程终止/线程中断/守护线程

/** * 线程:是进程的一个执行路径,共享一个内存空间,线程之间可以自由切换,并发执行,一个进程最少有一个进程(单线程程序) * 多线程两种实现方法:1.继承Thread类 2.实现Runnable接口 */ public class Main { public static void main(String[] args) { MyThread t1 = new MyThread(); t1.start(); //启动线程 System.out.println("main"); S

Java并发学习之六——等待线程的终结

本文是学习网络上的文章时的总结,感谢大家无私的分享. 1.在某些情况下,我们需要等待线程的终结.例如,我们可能会遇到程序在执行前需要初始化资源.在执行剩下的代码之前,我们需要等待线程完成初始化任务.为了达到此目的,我们使用Thread类的join()方法.当前线程调用某个线程的这个方法时,它会暂停当前线程,直到被调用线程执行完成. 2.Java提供2种形式的join()方法: Join(longmilliseconds) Join(long milliseconds,long nanos) 第一

Thread类线程结束会唤醒使用其对象做锁而睡眠的线程

首先回顾一下我们的基础知识. sleep: 线程睡眠,不会释放锁 wait: 线程等待.释放锁. notity: 唤醒随机一个当前对象等待的线程,并不会释放锁 notityAll: 唤醒所有当前对象等待的线程,并不会释放锁 遇到问题: 代码如下: package com.zhen.ten_chapter.question; /** * @author zhen * @Date 2019/4/18 10:17 */ public class ThreadDemo { public static v