第二章 线程管控

第二章 线程管控

线程的基本管控

  • 发起线程

    void do_some_work();
    std::thread my_thread(do_some_work);
    
    class background_task
    {
        public:
        void operator()()const 
        {
            do_something();
            do_something_else();
        }
    };
    background_task f;
    std::thread my_thread(f);//f将会被复制到属于新线程的存储空间中,由新线程调用
    
    std::thread mythread([]{
        do_something();
        do_something_else();
    });
    

    若程序不等待线程结束,在线程运行结束前,需保证它所访问的外部数据始终正确,有效。

    struct func
    {
        int &i;
        func(int&i_):i(i_){}
        void operator()()
        {
            for(unsigned j=0;j<100000;++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函数可能已经结束,而新线程还在继续运行,some_local_state已被销毁,新线程访问谁?
        //解决方式:1.汇合新线程2.将数据完全复制
    }
    
    • 等待线程完成(join())

    • 在出现异常情况下等待

      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(...)//若捕捉到异常,则调用join(),确保线程在f()结束前终结。
          {
              t.join();
              throw;
          }
          t.join();
      }
      
      class thread_guard
      {
          std::thread &t;
          public:
          explicit thread_guard(std::thread&t_):t(t_){}
          ~thread_guard()
          {
              if(t.joinable())
              {
                  t.join();
              }
          }
          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(my_func);
          //当主线程执行到f()末尾时,按构建的逆序,销毁局部对象。
          thread_guard g(t);//实例g在f()函数结束时会被析构,调用join()。
          do_something_in_current_thread();//即使该函数抛出异常,也会在析构时确保join()被调用。
      }
      
      • 在后台运行线程(detach())

        std::thread t(do_background_work);
        t.detach();
        assert(!t.joinable())//如果条件成立不进行任何操作,反之打印出异常信息。
        
        //分离线程处理新文件
        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);
                }
            }
        }
        

向线程函数传递参数

  • 方式:直接向std::thread的构造函数添加更多参数。
    线程具有内部存储空间,参数会按照默认方式先复制到该处,新创建的执行线程才能直接访问它们。然后,这些副本被当成临时变量,以右值形式传给新线程上的函数或可调用对象。即使函数的相关参数按设想应该是引用。

    void f(int i,std::string const&s);
    std::thread t(f,3,"hello");//字符串字面内容以char const*的形式传入,在新线程内部转为string。
    

    问题:主线程已经结束并销毁空间,而转换未及时进行,转换指向已经销毁的内容?

    解决方式:构造一个临时对象,提前转换。std::thread t(f,3,string("hello"));

若想要 non const 引用。

std::thread t(update_data_for_widget,w,std::ref(data));//传入data并非副本,而是引用。

类的成员函数设定为线程函数:

class X
{
    public:
    void do_lengthy_work();
};
X my_x;
std::thread t(&X::do_lengthy_work,&my_x);

unique_ptr指针

void process_big_object(std::unique_ptr<big_object>);
std::unique_ptr<big_object>p(new big_object);
p->prepare_data(42);
std::thread t(process_big_object,std::move(p));

在调用std::thread 的构造函数时,依据std::move§所指定的操作,big_object对象的归属权会发生转移,先进入新创建的线程内部存储空间,再转移给process_big_object()函数。

std::move§未进行移动操作,仅仅将p转换成右值;创建线程时,发生一次转移(由线程库内部进行复制);process_big_object()开始执行时,再次转移(函数调用复制参数)。

移交线程归属权

std::thread支持移动操作的意义是,函数可以便携的向外部转移线程的归属权

//从函数内部返回std::thread对象
std::thread f()
{
    void some_function();
    return std::thread(some_function);
}
//归属权转移到函数内部
void f(std::thread t);
void g()
{
    void some_function();
    f(std::thread(some_fuction));
    std::thread t(some_function);
    f(std::move(t));
}

新类scoped_thread,在离开其对象的所在作用域前,确保线程已经完结。

class scoped_thread
{
    std::thread t;
    public:
    explicit socped_thread(std::thread t_):t(std::move(t_))
    {
        if(!t.joinable())
        {
            throw std::logic_error("No thread");
        }
    }
    
    ~scoped_thread()
    {
        t.join();
    }
    
    scoped_thread(scoped_thread const &)=delete;
    scoped_thread& operator=(scoped_thread const &)=delete;
};

在thread_guard中:析构函数需判断线程是否依然可汇合。

class joining_thread
{
    std::thread t;
  public:
    joining_thread()noexcept=default;
    template<typename Callable,typename ...Args>
    explicit joining_thread(Callable&&func,Args &&...args):
    	t(std::forward<Callable>(func),std::forward<Args>(args)...)//给线程传参
    {}
    explicit joining_thread(std::thread t_)noexcept:t(std::move(t_))
    {}
    
    joining_thread(joining_thread&&other)noexcept:t(std::move(other.t))
    {}
    
    joining_thread& operator=(joining_thread&&other)noexcept
    {
        if(joinable())
            join();
        t=std::move(other);
        return *this;
    }
    
    ~joining_thread()noexcept
    {
        if(joinable())
            join();
    }
    
    void swap(joining_thread&other)noexcept
    {
        t.swap(other.t);
    }
    
    std::thread::id get_id()const noexcept
    {
        return t.get_id();
    }
    
    bool joinable()const noexcept
    {
        return t.joinable();
    }
    
    void join()
    {
        t.join();
    }
    
    void detach()
    {
        t.deatach();
    }
    
    std::thread&as_thread()noexcept
    {
        return t;
    }
    
    //const 调用该版本,effective C++
    const std::thread&as_thread()const noexcept
    {
        return t;
    }
};

在运行时选择线程数量

std::thread::hardware_concurrency();

//并行版的 std::accumulate()的简单实现
template<typename Iterator,typename T>
struct accumulate_block
{
    void operator()(Iterator first,Iterator last,T&result)
    {
        result=std::accumulate(first,last,result);
    }
};

template<typename Iterator,typename T>
T parallel_accumulate(Iterator first,Iterator last,T init)
{
   	unsigned long const length=std::distance(first,last);
    if(!length)
        return init;
    unsigned long const min_per_thread=25;//每个线程处理最低定量为25
    unsigned long const max_threads=(length+min_per_thread-1)/min_per_thread;//线程的最大数量
    unsigned long const hardware_threads=std::thread::hardware_concurrency();//硬件支持量
    unsigned long const num_threads=std::min(hardware_threads!=0?hardware_threads:2,max_threads);//运行线程不应超过硬件支持数
    unsigned long const block_size=length/num_threads;//各线程需要分担的元素数量
    
    std::vector<T>results(num_threads);//存放每个线程结果
    std::vector<std::thread> threads(num_threads-1);//存放线程,主线程算一
    
    Iterator block_start=first;
    
    for(unsigned long i=0;i<(num_threads-1);++i)
    {
        Iterator block_end=block_start;
        std::advance(block_end,block_size);
        threads[i]=std::thread(
        	accumulate_block<Iterator,T>(),block_start,block_end,std::ref(results[i])
        );
        block_start=block_end;
    }
    
    accumulate_block<Iterator,T>()(
    block_start,last,results[num_threads-1]);//主线程处理最后一小块
    
    for(auto&entry:threads)
    {
        entry.join();
    }
    return std::accumulate(results.begin(),results.end(),init);
}

识别线程

  • std::thread t(do_something); t.get_id();
  • std::this_thread::get_id();
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值