【C++并发实战】(二)线程管理
前一篇没用markdown编辑器感觉不好看,删了重新发
本篇主要讲述线程的管理,主要包括创建和使用线程
启动线程
线程出现是为了执行任务,线程创建时会给一个入口函数,当这个函数返回时,该线程就会退出,最常见的main()函数就是主线程的入口函数,在main()函数返回时主线程就结束了。
如何启动一个线程呢?就如上所述,需要给线程对象一个入口函数。
#include <iostream>
#include <thread>
using namespace std;
void hello()
{
cout << "hello world";
}
int main()
{
thread t(hello);
t.join();
return 0;
}
上述代码中实现了一个简单的线程
一旦线程被启动,就必须要显式地决定是要等待线程完成还是让线程自己运行,这个决定必须要在线程对象销毁前完成,否则,程序将会被终止(抛出异常),如果不等待线程完成,则需要保证线程访问的数据必须是有效的,直到该线程终止,在这种情况下,必须要小心的使用局部变量,局部变量的指针和引用。
PS:需要注意的是等待线程完成和分离线程必须要在线程对象销毁前完成,并不是在线程函数执行结束前完成。原因是线程对象被销毁后不能直接与线程通信,将无法等待或者分离。
例如:
#include <iostream>
#include <thread>
#include <stdlib.h>
#include <windows.h>
using namespace std;
void hello()
{
cout << "hello world";
}
int main()
{
thread t(hello);
Sleep(3000);
t.join();
return 0;
}
这个代码中线程执行的hello函数已经结束,依然可以join
等待线程完成
在上面的代码例子中,使用了线程对象的join函数,这个函数的作用就是等待线程完成。同时需要注意,一个线程不能被join两次。可以通过线程对象的joinable()函数判断当前函数是否可以被join。
std::thread t(do_thread_work);
if(t.joinable())
t.join();
分离线程
除了可以等待线程完成,还可以分离线程,让线程自己运行,也就是所谓的在后台运行线程,当线程对象被销毁后,线程可能仍在运行,此时,没有直接的方法可以与其通信,不能再通过线程对象获取到该线程,也不能再被join,所以要注意资源的请求和释放。
参照守护进程的概念,被分离的线程通常被称作守护线程。
std::thread t(do_some_background_work);
t.detach();
assert(!t.joinable());
为了分离线程,线程对象必须与一个线程相关联,不能在没有线程关联的线程对象上使用detach(),join()函数也是同理,因此也可以使用joinable()函数来判断当前函数是否可以detach。
在异常环境下的等待
如上所述,必须在线程对象被销毁之前调用join()或者detach(),如果需要分离线程,一般会在线程对象创建好后立刻调用,这样问题不大。但是如果打算等待线程完成,就需要考虑在哪个位置调用join。如果在线程开始之后join执行之前发生了异常,对join的调用可能就会被跳过。所以应该确保join函数被调用,以免程序被终止。
std::thread t(my_function);
try
{
do_something();
}
catch
{
t.join();
throw();
}
t.join();
当然还有其他的做法,利用RAII的思想,类似智能指针,对thread对象进行封装。
#include <iostream>
#include <thread>
class thread_guard
{
public:
explicit thread_guard(std::thread& t)
:t_(t)
{}
~thread_guard()
{
if (t_.joinable())
{
t_.join();
}
}
private:
std::thread& t_;
};
void hello()
{
std::cout << "hello world";
}
int main()
{
std::thread t(hello);
thread_guard g(t);
return 0;
}
传递参数给线程函数
线程的入口是个函数,作为使用者肯定是想传递一些函数参数的。操作也比较简单,将额外的参数传递给std::thread的够凹函数就可以了。但是,需要重视的一点是,参数默认上会被复制到内部存储空间,然后在那新创建的线程可以访问这些参数,即便函数中的相应参数期待着引用
这个意思就是说,传递参数给县城函数就像是给普通函数传递参数一样,默认传递的是形参,而且就算你加了引用,传递的也是形参。是std::thread的构造函数做的这个事情,他无视函数所期望的引用盲目的复制了所提供的值
解决方案也很简单,使用std::ref()包裹你想要以引用传递的参数。
void update_data_for_widget(widget_id w, widget_data& data);
void fun(widget_id w)
{
widget_data data;
std::thread t(update_data_for_widget, w, std::ref(data));
}
如果不这么做,传递的data在线程函数中的改变是不会起作用的。
转移线程的所有权
和std::unique_ptr类似,std::thread是可移动并且非可复制的,这意味着一般不会有两个线程对象指向同一个线程实例,如果出现此情况会发生异常。如果同时给一个线程对象关联两个线程实例,也会触发异常。
void some_function();
void some_other_function();
std::thread t1(some_function);
std::thread t2 = std::move(t1);
t1 = std::thread(some_other_function);
t3 = std::move(t2);
t1 = std::move(t3); //此项操作将会终止程序!
标识线程
线程标识符是std::thread::id类型的,获取方式有以下两种
- 可以通过与线程相关联的std::thread对象调用get_id()方法获取。如果该线程对象没有相关联的线程,则此方法会返回一个默认构造的std::thread::id对象用来表示“没有线程”
- 另外可以通过std::this_thread::get_id()获取当前现成的id。
id可以自由地被用于复制和比较,或者是被排序。
【C++并发实战】(二)线程管理的更多相关文章
- day31_8.12并发编程二线程
一.进程间的通信 在进程中,可以通过Queue队列进行通信,队列有两个特点: 1.先进先出.(先来的数据先被取出) 2.管道式存取.(数据取出一次后就不会在有了) 在python中有以下方法来操作数据 ...
- java并发系列(二)-----线程之间的协作(wait、notify、join、CountDownLatch、CyclicBarrier)
在java中,线程之间的切换是由操作系统说了算的,操作系统会给每个线程分配一个时间片,在时间片到期之后,线程让出cpu资源,由其他线程一起抢夺,那么如果开发想自己去在一定程度上(因为没办法100%控制 ...
- Java并发(二十):线程本地变量ThreadLocal
ThreadLocal是一个本地线程副本变量工具类. 主要用于将私有线程和该线程存放的副本对象做一个映射,各个线程之间的变量互不干扰,在高并发场景下,可以实现无状态的调用,特别适用于各个线程依赖不同的 ...
- Scala 深入浅出实战经典 第68讲:Scala并发编程原生线程Actor、Cass Class下的消息传递和偏函数实战解析
王家林亲授<DT大数据梦工厂>大数据实战视频 Scala 深入浅出实战经典(1-87讲)完整视频.PPT.代码下载: 百度云盘:http://pan.baidu.com/s/1c0noOt ...
- Android线程管理(二)——ActivityThread
线程通信.ActivityThread及Thread类是理解Android线程管理的关键. 线程,作为CPU调度资源的基本单位,在Android等针对嵌入式设备的操作系统中,有着非常重要和基础的作用. ...
- android 进程/线程管理(二)----关于线程的迷思
一:进程和线程的由来 进程是计算机科技发展的过程的产物. 最早计算机发明出来,是为了解决数学计算而发明的.每解决一个问题,就要打纸带,也就是打点. 后来人们发现可以批量的设置命令,由计算机读取这些命令 ...
- Java并发(二十一):线程池实现原理
一.总览 线程池类ThreadPoolExecutor的相关类需要先了解: (图片来自:https://javadoop.com/post/java-thread-pool#%E6%80%BB%E8% ...
- 并发和多线程(二)--线程安全、synchronized、CAS简介
线程安全性: 当多个线程访问一个类的时候,这个类始终表示出正确的行为,那么这个类是线程安全的. 无状态的对象一定是线程安全的,例如大部分service.dao.Servlet都是无状态的. 线程安全体 ...
- ios 多线程开发(二)线程管理
线程管理 iOS和OS X中每一个进程(或程序)由一个或多个线程组成.程序由一个运行main方法的线程开始,中间可以产生其他线程来执行一些指定的功能. 当程序产生一个新线程后,这个线程在程序进程空间内 ...
- Java并发编程:线程池的使用
Java并发编程:线程池的使用 在前面的文章中,我们使用线程的时候就去创建一个线程,这样实现起来非常简便,但是就会有一个问题: 如果并发的线程数量很多,并且每个线程都是执行一个时间很短的任务就结束了, ...
随机推荐
- Commands that may modify the data set are disabled, because this instance is configured to report errors during writes if RDB snapshotting fails (stop-writes-on-bgsave-error option)
今天运行Redis时发生错误,错误信息如下: org.springframework.dao.InvalidDataAccessApiUsageException: MISCONF Redis is ...
- (Lua) C++ 寫函式,Lua 呼叫使用
C++ 裡頭寫一個 Function 但是透過 Lua 提供input參數並且回傳結果給Lua 輸出的辦法. Lua 檔案 , , ) print("ave : ", ave, & ...
- python 全栈开发:基础复习
格式化输出: username = 'python' password = 'python1' print(''' username:%s password:%s '''%(username,pass ...
- springboot(十四)-分库分表-自动配置
上一节我们是手动配置数据源的,直接在java代码里写数据库的东西,这操作我个人是不喜欢的.我觉得这些东西就应该出现在application.yml文件中. 还有,万一我们的项目在使用之后,突然需要改变 ...
- docker仓库harbor镜像列表获取命令
2019-03-14 搭建好docker仓库harbor之后,在服务器上获取harbor的所有镜像列表命令 curl -u "harbor账号:密码" -X GET -H &quo ...
- Mac下的裁剪快捷键
按[Command]+[C]复制文件,然后按[Command]+[Option]+[V]. 注意:[Command]+[X]只能剪切文字文本.
- Could not find a version that satisfies.... No matching distribution found for .....
原文作者:aircraft 原文链接:https://www.cnblogs.com/DOMLX/p/10227403.html 今天在安装mysql-python的时候报了很多的错误,其中一条就是这 ...
- SpringMVC 的工作机制
在一个工程中如果想要使用 SpringMVC的话,只需要两个步骤 在web.xml中配置一个DispatcherServlet.需要配置一个org.springframework.web.servle ...
- 多线程编程(一)-Semaphore(信号量)的使用
Semaphore的介绍 单词Semaphore的中文含义就是信号.信号系统的意思,此类的主要作用就是限制线程并发的数量. 举个例子,一个屋子里有10个人,但只有一个窄门可以出去,这个窄门一次最多只能 ...
- 快捷键 -- windows
win+数字 : 打开任务栏第n个图标 Win+D :快速显示桌面 Win+R :快速运行打开软件 例如 cmd services,msc Win+E:打开资源管理器 Win+L:快速锁定计算机 ...