zoukankan      html  css  js  c++  java
  • 管理线程之创建线程

    主要的线程管理包含:1、创建线程。2、等待它结束或在后台执行。

    3、向线程函数传递參数。更改线程对象全部权。4、选择线程和使用特定线程。

    创建线程

    线程在创建线程对象时開始执行,创建线程对象使用std::thread。像上节最后那个函数一样。最简单的情况是线程对象创建后执行一个无返回值、无參数的函数。

    这个函数在线程中执行直到返回,这时线程终止。

    想一下最复杂的情况,线程执行的函数可能是个函数对象。须要传递參数,执行一系列独立的操作。这些操作须要系统的一些信息,直到接收到某个信号终止。

    线程执行什么操作或者从哪里開始并不重要,開始一个线程的方法归根究竟就是创建线程对象 std::thread object:

    void do_some_work();
    std::thread my_thread(do_some_work);
    这是最简单的情况。std::thread还能够使用可调用类型(callable type)。能够在创建线程对象时,在调用函数处传递一个对象的实例。

    class background_task
    {
    public:
    	void operator()()const
    	{
    		do_something();
    		do_something_else();
    	}
    };
    background_task f;
    std::thread my_thread(f);
    在这样的情况下,函数对象在新创建线程时被复制到新建线程的内存中,并在这開始调用。必须确保拷贝行为等同于原始对象,否则得不到预期的结果。

    在向线程构造函数传递函数对象时要避免被称之为”C++最令人烦的语法“。假设你传递一个暂时对象取代命名变量,语法上和一个函数的声明一模一样,这时编译器会把它当做函数声明,而不会解释成对象定义。比如:
    std::thread my_thread(background_task());
    声明了一个函数my_thread。这个函数有一个參数(指向函数的指针。返回background_task对象)。返回一个std::thread对象。而不是创建新线程。这时能够通过3种方法来避免:1像上面那样命名函数对象。2使用额外的圆括号。

    3、使用新的标准初始化语法。

    第2中方法
    std::thread my_thread((background_task()));
    加上额外的圆括号后,会阻止编译器把它解释成函数的声明,这样就能把my_thread解释成定义std::thread类型对象了。
    第3中方法
    std::thread my_thread{ background_task() };
    使用新的标准初始化语法,用花括号而不是圆括号。

    一种类型:可调用对象(callable object)能够避免上面的问题是使用拉姆达表达式(lambda expression)。这是C++11的新特性,它同意使用拒不函数,捕捉局部变量,避免传入參数。上面样例能够写成
    std::thread my_thread([](
    		do_something();
    		do_something_else();
    	});

    一旦你開始你的线程,你必须显示的决定是否等待它结束(调用join)或者让它独立同意(调用detach)。假设不在线程对象销毁之前决定,你的线程会终止(std:thread的析构函数调用std::terminate())。确保你的线程正确的终止或分离,即使在有异常的情况发送,这很重要。

    注意。你要在线程对象析构前决定--在你做决定时线程可能早已终止或分离,假设你分离它。在线程对象析构后它还可能执行。

    假设你不等待线程结束。那么要确保线程用到的数据一直有效直到线程结束。这不是什么新问题。即使单线程的代码页有这种问题。(在《Linux多线程server编程》中。有讲到过这个问题,使用智能指针)。

    一个情形就是函数使用了局部对象的指针或引用,当函数终止时线程并没有终止。

    以下看这个样例:

    struct func
    {
    	int &i;
    	func(int &i_)i:(i_){}
    	void operator()()
    	{
    		for(unsigned j=0; j <= 1000000; ++j)
    		{
    			do_something(i);
    		}
    	}
    };
    void oops()
    {
    	int some_local_state=0;
    	func my_func(some_local_state);
    	std::thread my_thread(my_func);
    	my_thread.detach();
    }
    在这个样例中。新线程在oops函数终止后可能还会执行(分离了新线程),假设线程还在执行,那么函数do_something(i)会使用已经销毁的变量。
    一个常见的避免上面问题的方法是使线程函数独立,拷贝这些数据到线程而不是共享。假设使用可调用类型对象(callable object)。那么把对象拷贝到对象里,原始对象马上销毁。可是,此时你还要注意对象是否包括指针或引用。在线程函数内使用局部变量是个坏注意,除非你能确保函数一直存在知道线程运行结束(在函数内调用join等待线程)。
  • 相关阅读:
    windows平台部署.netcore和vue项目
    .netcore系统权限认证
    全文检索 识别pdf 图片OCR识别
    搜索引擎solr的安装与配置
    SQLSugar动态拼接Lambda表达式(顺便提一个sqlsugar框架的bug)
    .netcore项目部署linux
    vue+element 部署linux服务器
    使用七牛云存储上传文件学习案例
    MSSQL 全库搜索 指定字符串
    系统右键自定义功能-右键备份【C#】
  • 原文地址:https://www.cnblogs.com/gavanwanggw/p/7081699.html
Copyright © 2011-2022 走看看