boost | 线程与并发(二)thread

目录

一、Mutex互斥量

1.成员函数

2.扩展(包装mutex)

(1)lock_guard

(2)unique_lock

 3.自定义互斥量

二、thread线程对象

1.创建线程对象

2.等待线程

3.分离线程

4.线程辅助类thread_guard(包装thread)

5.RAII线程类scoped_thread

6.中断线程

7.线程组

8.call_once

9.线程结束时执行操作

三、条件变量

四、异步操作线程返回值获取

1.future

2.async()函数

3. shared_future


        thread库为C++增加了线程处理的能⼒,它提供简明清晰的互斥量、线程、条件变量等概念,⽤ 它可以很容易地创建多线程应⽤程序。thread库是⾼度可移植的库,它兼容C++标准,⽀持使⽤ 最⼴泛的POSIX线程和Windows线程,⽤它编写的代码⽆须修改就可以在UNIX、Windows等操作 系统上编译运⾏。

一、Mutex互斥量

        互斥量(mutex)是⼀种⽤于线程同步的⼿段,它可以在多线程环境⾥防⽌多个线程同时操作共享资源。⼀旦⼀个线程锁定了互斥量,那么其他线程必须等待它解锁互斥量后才能访问共享资 源。

thread提供了6种互斥量类型(除了互斥功能不同,它们的基本接⼝都很相似):

  • null_mutex:⽆任何锁定功能的互斥量,它是空对象模式的应⽤。
  • mutex:独占式的互斥量,是最简单、最常⽤的互斥量类型。
  • timed_mutex:独占式的互斥量,但提供超时锁定功能。
  • recursive_mutex:递归式互斥量,可以多次锁定,相应地也需要多次解锁。
  • recursive_timed_mutex:递归式互斥量,同样增加了超时锁定功能。
  • shared_mutex:multiple-reader/single-writer型的共享互斥量(⼜称读写锁)。

1.成员函数

        成员函数lock()⽤于线程阻塞等待直⾄获得互斥量的所有权(锁定);

        当线程使⽤完共享资源后应该及时使⽤unlock()解除对互斥量的锁定。
        try_lock()尝试锁定互斥量,如果锁定成功则返回true,否则返回false,它是⾮阻塞的。

        直接使⽤mutex的成员函数来锁定互斥量不够⽅便,⽽且在发⽣异常导致退出作⽤域等情况下, 很可能会导致未调⽤unlock(),所以通常要使⽤try-catch块保证解锁互斥量。

//--------------------------.h文件
#pragma once
#define BOOST_THREAD_VERSION 5
#include <iostream>
#include <vector>
#include <map>
#include <boost/lexical_cast.hpp>
#include <boost/thread.hpp>
#include <boost/thread/lock_factories.hpp>
#include <boost/thread/lockable_adapter.hpp>
#include <boost/thread/thread_guard.hpp>
#include <boost/thread/scoped_thread.hpp>
#include <boost/thread/shared_lock_guard.hpp>

class StudyThreadClass
{
public:
    void Test();

private:
    std::map<int,std::string> _map;

    std::vector<boost::thread> thread_vec;

    void MutexStudy(int threadId);
};
//--------------------------.cpp文件

boost::mutex _mutex;

void StudyThreadClass::Test()
{
    for (size_t i = 0; i < 10000; i++)    //初始化vector
    {
        _map[i] = "hello world";
    }

    //to do 创建线程调用MutexStudy
}
void StudyThreadClass::MutexStudy(int threadId)
{
    while (_map.size() > 0) {
        try {
            _mutex.lock();

            std::string msg = boost::lexical_cast<std::string>(index);
            _map[index] = msg;
            std::cout << "线程id:" << threadId << "," << _map[index] << "," << _map.size() << std::endl;
            _map.erase(index);
            index++;
            Sleep(10);

            _mutex.unlock();
        }
        catch (...)        //使用try-catch保证解锁互斥量
        {
            _mutex.unlock();
        }
    }
}

        成员函数try_lock_for()和try_lock_until()为try_lock()增加了时间等待的功能:在这段时间内如果获得了锁那么函数会⽴即返回true,否则超时返回false,表示未获得锁,线程此时就可以去做其他的事情。等待时间可以是从当前开始的相对时间(时间⻓度),也可以是⼀个绝对时间点。

void StudyThreadClass::MutexStudy(int threadId){
     while (_map.size() > 0) {
         boost::chrono::milliseconds _milliseconds(200);
         bool flag = _mutex.try_lock_for(_milliseconds);
         if (flag) {
             std::string msg = boost::lexical_cast<std::string>(index);
             _map[index] = msg;
             std::cout << "线程id:" << threadId << "," << _map[index] << std::endl;
             _map.erase(index);
             index++;
             _mutex.unlock();
             Sleep(100);
         }
         else {
             std::cout << "阻塞...线程id:" << threadId << "," << index << std::endl;
         }
     }
} 

2.扩展(包装mutex)

(1)lock_guard

        thread库提供RAII型的lock_guard类⽤于辅助锁定互斥量。它在构造时锁定互斥量,在析构时⾃ 动解锁,从⽽保证了互斥量的正确操作,避免遗忘解锁,它就像⼀个智能指针。

void StudyThreadClass::MutexStudy(int threadId){
     while (_map.size() > 0) {
         boost::lock_guard<boost::mutex> _lock_guard(_mutex); //注意对比,不需要解锁

         std::string msg = boost::lexical_cast<std::string>(index);
         _map[index] = msg;
         std::cout << "线程id:" << threadId << "," << _map[index] << std::endl;
         _map.erase(index);
         index++;
         Sleep(100);
     }
}

(2)unique_lock

        unique_lock的⼯作机制与lock_guard相同,它也使⽤了RAII在构造函数⾥锁定,在析构函数⾥解 锁,但它的构造函数还可以接收其他的锁定选项,从⽽有不同的⾏为。

        unique_lock也不允许拷⻉,但它内部使⽤指针⽽不是引⽤来保存互斥量,这使得它可以被转移, ⽤起来更灵活。 unique_lock还拥有与mutex相同的接⼝,所以允许程序员任意控制锁定/解锁的时机,⽽且最终 unique_lock的析构函数会保证正确处理互斥量。

        thread库⾥有3个锁定选项,它们都是空类,仅作为运⾏时的标志,此外没有其他作⽤,在unique_lock的构造函数⾥使⽤下列常量标志就可以使unique_lock产⽣不同的效果,可以在后续执⾏其他的锁定/解锁操作(在并发环境不可以用这三个锁定选项)。

  • defer_lock:is_locked=false,不执⾏锁定操作。
  • try_to_lock:is_locked=false,执⾏try_lock。
  • adopt_lock:is_locked=true,不执⾏锁定操作。
void StudyThreadClass::MutexStudy(int threadId){
     while (_map.size() > 0) {
         boost::unique_lock<boost::mutex> _unique_lock(_mutex); //注意对比,也不需要解锁
         std::string msg = boost::lexical_cast<std::string>(index);
         _map[index] = msg;
         std::cout << "线程id:" << threadId << "," << _map[index] << std::endl;
         _map.erase(index);
         index++;
         Sleep(100);
     }
}

        因为unique_lock⽀持转移语义,所以它可以从⼯⼚函数make_unique_lock产⽣,利⽤模板函数⾃动推导类型的特性 可以不必写出模板参数,这进⼀步简化了编写代码的⼯作 。

void StudyThreadClass::MutexStudy(int threadId){
     while (_map.size() > 0) {
         auto _unique_lock= boost::make_unique_lock(_mutex); //注意对比
         std::cout << "是否锁定了互斥量:" << _unique_lock.owns_lock() << std::endl;
         std::string msg = boost::lexical_cast<std::string>(index);
         _map[index] = msg;
         std::cout << "线程id:" << threadId << "," << _map[index] << std::endl;
         _map.erase(index);
         index++;
         Sleep(100);
     }
}

 3.自定义互斥量

        lock_guard和unique_lock⼤多数情况下需要搭配mutex使⽤,⽤于锁定互斥量,但因为它们是模板类,所以只要是符合“Lockable”概念——也就是有lock/unlock/try_lock接⼝的类(不⼀定是互 斥量),都可以⽤于lock_guard和unique_lock,这样就能够很容易地锁定整个对象,实现原⼦性的事务操作。

         thread库定义了⼏个lockable适配器类,⽅便我们实现Lockable概念。

  • basic_lockable_adapter:最简单的接⼝,提供lock/unlock。
  • lockable_adapter:基本的接⼝,增加try_lock功能。
  • timed_lockable_adapter:增加try_lock_for/try_lock_until。

        适配器类需要在模板参数⾥指定要适配的mutex类型,以继承的⽅式使⽤,⼦类⾃动获得它们的 lock接⼝,然后就可以应⽤于lock_guard和unique_lock。

class MyLockableAddapter :public boost::lockable_adapter<boost::mutex> {
//可以是空类。使⽤lockable_adapter<mutex>作为基类,所以它具有了和mutex相同的接⼝,可以像使⽤⼀个互斥量那样使⽤它
private:
    int value = 0;
public :
    void Add(int i)
    {
        value += i;
     }
    void Sub(int i)
    {
        value -= i;
    }
    int GetValue()
    {
        return value;
    }
};
MyLockableAddapter myLockableAddapter;
void StudyThreadClass::MutexStudy(int threadId){
       auto _unique_lock = boost::make_unique_lock(myLockableAddapter);
       while (myLockableAddapter.GetValue() == 0)
       {
           std::string msg = boost::lexical_cast<std::string>(index);
           _map[index] = msg;
           std::cout << "线程id:" << threadId << "," << _map[index] << std::endl;
           _map.erase(index);
           index++;
           myLockableAddapter.Add(10);
           Sleep(100);
       }
}

二、thread线程对象

        thread类实现了操作系统⾥的线程表示,负责启动和管理线程对象,它在概念和操作上都与 POSIX线程很相似。thread对象是不可拷⻉、不可⽐较的,但它⽀持转移(move)语义,有转移构造函数和转移赋值函数,所以它可以从⼯⼚函数产⽣。

        成员函数get_id()可以返回thread::id对象,它提供了完整的⽐较操作符和流输出操作,唯⼀ 地标识了thread对象。

        静态成员函数hardware_concurrency()可以获得系统可并发的线程数量,即CPU内核数量。

        静态成员函数physical_concurrency()可以获得真实的物理CPU核⼼数,这两个函数如果⽆法获取信息则 都返回0。

        4个⾃由函数get_id()、yield()、sleep_for()和sleep_until(),它们⽆须使⽤thread 对象就可以操作当前线程,但它们位于⼦名字空间boost::this_thread,⽽不是thread的静态成 员函数。

  • get_id():与thread同名的函数,⽤来获得thread::id。
  • yield():指示当前线程放弃时间⽚,允许其他的线程运⾏。
  • sleep_for():线程睡眠等待⼀⼩段时间。
  • sleep_until():线程睡眠等待⾄⼀个时间点
void StudyThreadClass::MutexStudy(int Id){
//...
}
void Test{
    boost::thread thread(&StudyThreadClass::MutexStudy, this, 1);
    std::cout << "thread_id:" << thread.get_id() << std::endl;
    std::cout <<"cpu内核:"<< thread_vec.hardware_concurrency() << std::endl;
    std::cout <<"物理cpu核心数:"<< thread.physical_concurrency() << std::endl;
}

        从某种程度上来说,线程就是在进程的另⼀个空间⾥运⾏的⼀个函数,因此线程的创建需要传递给thread对象⼀个⽆参的可调⽤物——函数、函数对象、bind表达式、lambda表达式,它必须具有operator()以供线程执⾏。

        如果可调⽤物不是⽆参的,那么thread的构造函数也⽀持直接传递所需的参数,这些参数将被拷⻉并在发⽣调⽤时传递给函数。

        在传递参数时需要注意,thread使⽤的是参数的拷⻉,因此要求可调⽤物和参数类型都⽀持拷⻉构造。如果希望将参数传递给线程引⽤值就需要使⽤ref库进⾏包装,同时必须保证被引⽤的对象在线程执⾏期间⼀直存在,否则会引发未定义⾏为。

        thread对象在析构时会调⽤std::terminate结束线程的执⾏,它并不关⼼线程是否执⾏完毕,所 以如果要保证函数正确运⾏必须调⽤join()等待线程执⾏结束,或者调⽤detach()分离线程体。

1.创建线程对象

        当成功创建了⼀个thread对象后,线程就⽴刻开始执⾏,thread不提供类似start( )、begin( )那样的⽅法。

2.等待线程

        thread的成员函数joinable()可以判断thread对象是否标识了⼀个可执⾏的线程体。如果 joinable()返回true,我们就可以调⽤成员函数join()或try_join_for()/try_join_until()来阻塞等待线程执⾏结束。两者的区别如下:

  • join()⼀直阻塞等待,直到线程结束。
  • try_join_for()/try_join_until()阻塞等待⼀定的时间段后,⽆论线程是否结束它都返回。注意,它不会⼀直阻塞等待到指定的时间⻓度,如果在这段时间⾥线程运⾏结束,即使时间未到它也会返回。 
void StudyThreadClass::Test()
{
    for (size_t i = 0; i < 10000; i++)    //初始化vector
    {
        _map[i] = "hello world";
    }
    for (size_t i = 0; i <10; i++)   //创建线程对象
    {
        thread_vec.push_back(boost::thread(&StudyThreadClass::MutexStudy, this, i)); 
    }

    for (size_t i = 0; i <10; i++)   //开线程
    {
        thread_vec[i].join();
    }
}

3.分离线程

        成员函数detach()将thread与线程执⾏体⼿动分离,此后thread对象不代表任何线程体, joinable()==false,从⽽失去对线程体的控制。因此当不需要再操作线程体时,我们就可以使⽤临时对象来启动线程,随即调⽤它的 detach( )。

4.线程辅助类thread_guard(包装thread)

        thread库在C++标准之外提供thread_guard辅助类,可以让我们⽅便地控制 thread对象析构时的⾏为。thread_guard类可以更明确地表示我们使⽤线程的意图,默认动作是join(),⽽且由于它使⽤了RAII,所以它更加安全。 

boost::thread _thread(&StudyThreadClass::MutexStudy, this, i)); 

boost::thread_guard<> _thread_guard(_thread);

5.RAII线程类scoped_thread

        thread库⾥还有⼀个与thread的接⼝和功能完全⼀致的线程类scoped_thread,它可以使⽤模板参数定制析构的动作,默认是join()动作。 scoped_thread的命名和scoped_ptr⼀样清晰,它的⽤法结合了thread和thread_guard。

boost::scoped_thread<>(&StudyThreadClass::MutexStudy, this, 1);

6.中断线程

        boost::thread有两个⾮C++标准的成员函数interrupt()和interruption_requested(),它们允许正在执⾏的线程被中断。

  • interrupt():要求线程中断执⾏。
  • interruption_requested():检查线程是否被要求中断。

        被中断的线程会抛出⼀个thread_interrupted异常,它是⼀个空类,不是std::exception或 boost::exception的⼦类。thread_interrupted异常应该在线程执⾏函数⾥被捕获并处理,如果线程不处理这个异常,那么默认的动作是中断线。

void StudyThreadClass::Test()
{
    for (size_t i = 0; i < 10000; i++)    //初始化vector
    {
        _map[i] = "hello world";
    }
    for (size_t i = 0; i < 10; i++)   
    {
        thread_vec.push_back(boost::thread(&StudyThreadClass::MutexStudy, this, i));
    }
    for (size_t i = 0; i < 10; i++)
    {
        thread_vec[i].interrupt();    //中断
        thread_vec[i].join();
    }
}

        线程不是在任意时刻都可以被中断的。thread库预定义了若⼲个线程的中断点,只有当线程执⾏到中断点的时候才可能被中断,⼀个线程可以拥有任意多个中断点。 thread库的中断点共12个,它们都是函数调⽤:

  • thread::join()
  • thread::try_join_for()/try_join_until()
  • condition_variable::wait()
  • condition_variable::wait_for()/wait_until()
  • condition_variable_any::wait()
  • condition_variable_any::wait_for()/wait_until()
  • this_thread::sleep_for()/sleep_until()
  • this_thread::interruption_point()

        在上述中断点中,前11个中断点都是某种形式的等待函数,表明线程在阻塞等待的时候可以被中 断。⽽最后⼀个位于⼦名字空间boost::this_thread的interruption_point()则是⼀个特殊的中 断点函数,它并不等待,只是起到⼀个“标签”的作⽤,表示线程执⾏到这个函数所在的语句就可以被中断。

        默认情况下线程都是允许中断的,但thread库允许进⼀步控制线程的中断⾏为。 ⼦名字空间boost::this_thread⾥提供了⼀组函数和类来共同完成线程的中断启⽤和禁⽤:

  • interruption_enabled()函数检测当前线程是否允许中断。
  • interruption_requested()函数检测当前线程是否被要求中断。
  • 类disable_interruption是⼀个RAII类型的对象,它构造时关闭线程的中断,析构时⾃动恢复线 程的中断状态。在它的⽣命期内线程始终是不可中断的,除⾮使⽤了restore_interruption对象。
  • restore_interruption只能在disable_interruption的作⽤域内使⽤,它在构造时临时启⽤线程的 中断状态,在析构时⼜关闭线程的中断状态。
void StudyThreadClass::MutexStudy(int threadId)
{
    try
    {
        while (_map.size() > 0) {

            _mutex.lock();
            std::string msg = boost::lexical_cast<std::string>(index);
            _map[index] = msg;
            std::cout << "线程id:" << threadId << "," << _map[index] << "," << _map.size() << std::endl;
            _map.erase(index);
            index++;

            if (_map.size() < 9995)
            {
                boost::this_thread::interruption_point();
                boost::this_thread::disable_interruption di;

                if (_map.size() < 9993)
                {
                    boost::this_thread::restore_interruption ri(di);
                    boost::this_thread::interruption_point();
                }
            }
            _mutex.unlock();
        }
    }
    catch (boost::thread_interrupted& e)//捕获线程中断异常
    {
        std::cout << "thread interrupted!" << std::endl;
    }
}

7.线程组

        thread库提供类thread_group⽤于管理⼀组线程,它就像⼀个“线程池”,它内部使⽤std::list< thread*>来容纳创建的thread对象。

        成员函数create_thread()是⼀个⼯⼚函数,它可以创建thread对象并运⾏线程,同时可以加⼊内部的list。注意它不⽀持像thread构造函数那样传递函数参数的⽤法,所以通常有必要使⽤bind 或lambda来包装执⾏的函数,当然我们也可以在thread_group外部创建线程对象,使⽤add_thread()加⼊线程组。

        如果不需要某个线程,remove_thread()可以删除list⾥的thread 对象。

        成员函数is_this_thread_in()和is_thread_in()可以判断当前线程或某个线程是否属于本线程组。

        join_all()和interrupt_all()⽤来操作list⾥的所有线程对象,等待或中断这些线程。

        thread_group内部使⽤shared_mutex来保护线程list,所以它本身是线程安全的,在多线程环境⾥可以放⼼使⽤。

void StudyThreadClass::Test()
{
    boost::thread_group _thread_group;
    _thread_group.create_thread(std::bind(&StudyThreadClass::MutexStudy, this, 0));
    _thread_group.create_thread(std::bind(&StudyThreadClass::MutexStudy, this, 1));
    _thread_group.join_all();
}

8.call_once

        为了保证初始化函数在多线程环境中被正确调⽤,thread库提供了仅调⽤⼀次的机制call_once, 使多个线程在操作函数时只能有⼀个线程成功执⾏,避免多次执⾏线程导致错误。

         在使⽤call_once()函数时我们必须预先声明once_flag对象,⽽不能使⽤临时变量(即不能在传入参数中创建变量),否则会引发编译错误。

eg:在之前的test()函数中,创建线程之前我们会初始化vector,如果改为在MutexStudy()函数在初始化,并且只在一个线程中初始化成功,需要这样写:


void StudyThreadClass::Init()
{
    std::cout << "init" << std::endl;
    for (size_t i = 0; i < 10000; i++)    //初始化vector
    {
        _map[i] = "hello world";
    }
}
void StudyThreadClass::MutexStudy(int threadId)
{
    static  boost::once_flag flag;
    boost::call_once(boost::bind(&StudyThreadClass::Init, this), flag);
    //boost::call_once(flag, boost::bind(&StudyThreadClass::Init,this));    //如函数有参数,用这种形式
    ...
}

9.线程结束时执行操作

        this_thread名字空间提供了⼀个at_thread_exit(func)函数,它允许“登记”⼀个线程在结束的时 候执⾏可调⽤物func,⽆论线程是否被中断。但如果线程被特定的操作系统API强⾏中⽌,或者程序调⽤标准C函数exit()(如main()函数 正常结束)、abort(),那么线程也会强制结束,at_thread_exit()登记的函数不会被调用。

三、条件变量

        (之前的数据回流sdk中就用到了这个机制,只有业务请求数据回流向sdk传入回流的数据后,sdk内部的线程才会发起请求进行上传,所以需要等待数据)

        条件变量是另⼀种⽤于等待的同步机制,它可以实现线程间的通信,它必须与互斥量配合使⽤, 等待另⼀个线程中某个事件发⽣(满⾜某个条件),然后线程才能继续执⾏。 thread库提供两种条件变量对象:condition_variable和condition_variable_any,⼀般情况下我们 应该使⽤condition_variable_any。

        拥有条件变量的线程先锁定互斥量,然后循环检查某个条件,如果不满⾜条件,就调⽤条件变量 的成员函数wait()等待,直⾄满⾜条件。其他线程处理条件变量要求的条件:当条件满⾜时调 ⽤它的成员函数notify_one()或notify_all(),以通知⼀个或所有正在等待条件变量的线程停 ⽌等待,继续执⾏。

        成员函数wait_for()和wait_until()的功能与wait()类似,但它们只等待⼀定的时间,如果 条件满⾜则结束等待,返回no_timeout值,如果条件未满⾜并且超时则返回timeout。

举个栗子:生产者-消费者

class StudyThreadClass
{
public:

    void ConditionVariableAnyStudy();

private:
    //存元素线程
    void PutElement();
    //条件变量
    boost::condition_variable_any  _condition_get;
    //取元素线程
    void GetElement(int threadId);
};
void StudyThreadClass::ConditionVariableAnyStudy()
{
    boost::thread_group _group;
    _group.create_thread(boost::bind(&StudyThreadClass::PutElement, this));//生产者
    _group.create_thread(boost::bind(&StudyThreadClass::GetElement, this,0));//消费者
    _group.create_thread(boost::bind(&StudyThreadClass::GetElement, this,1));//消费者
    _group.join_all();
}
int count = 0;
void StudyThreadClass::PutElement()
{
    while (true) {
        for (size_t i = 0; i < 1; i++)
        {
            count++;
            _queue.push(count);
            std::cout << "存入元素:"<<count << std::endl;
        }
        if (!_queue.empty()) {
            _condition_get.notify_all();
        }
        Sleep(20);
    }
}

void StudyThreadClass::GetElement(int threadId)
{
    while (true) {
        _mutex.lock();
        if (_queue.size() < 1) {
            _condition_get.wait(_mutex);
        }
        if (!_queue.empty()) {
            std::cout << threadId << " 取出元素:" << _queue.front() << std::endl;
            _queue.pop();
        }
        _mutex.unlock();
        Sleep(20);
    }
}

四、异步操作线程返回值获取

1.future

        很多情况下,线程不仅要执⾏⼀些⼯作,还可能会返回⼀些计算结果。thread库使⽤future范式提 供了异步操作线程返回值的⽅法,因为这个返回值在线程开始执⾏时是不可⽤的,该返回值是⼀ 个“未来”的“期待值”,所以它被称为future(期货)。

        future的成员函数wait()的⾏为类似thread.join(),它可以阻塞等待线程的执⾏,直⾄获得 future值。

        wait_for()/wait_until()增加了超时的功能,返回future_status枚举值表示是否执⾏ 成功。

        成员函数get()⽤来获得future值,它会默认调⽤wait()等待线程计算完毕。get()只能被调 ⽤⼀次,在获取值后再调⽤get()的⾏为是未定义的——通常会抛出异常。

        成员函数valid()/is_ready()、has_value()、has_exception()分别⽤来测试future是否 可⽤(后三个⾮C++标准)、是否有值、是否发⽣了异常,它们可以在wait()之后检查future的 状态,避免发⽣错误。

         future还有两个模板特化形式:future<T&>和future<void>,对于后⼀种形式⽽⾔,成员函数 get()将返回void值,等价于wait()。

2.async()函数

        async()函数⽤于产⽣future对象,它异步启动⼀个线程运⾏函数,返回future对象,这样随后我们就可以利⽤future获取计算结果。

         async()函数的⽤法很像thread,它们都可以启动⼀个线程,但调⽤async()得到的是future 对象⽽不是thread对象,因此async()更关⼼函数的计算结果⽽不是过程。当然,我们完全可以忽略async()的返回值,把它简单地当作⼀个线程启动器。

        策略枚举类launch可⽤于定制async()启动线程的时机:

  • none:⽆意义,不能使⽤这个枚举值,否则会导致运⾏错误。
  • async:要求⽴即启动⼀个线程执⾏函数。
  • deferred:只有当显式调⽤future的get()/wait()时才会启动线程。
  • any:async或deferred,不确定。
int StudyThreadClass::fab(int a)
{
  
    if (a == 0 || a == 1)
    {
        return 1;
    }
    return fab(a - 1) + fab(a - 2);
}
void StudyThreadClass::AsyncStudy()
{
    auto result_5 = boost::async(boost::bind(&StudyThreadClass::fab, this, 5));
    auto result_7 = boost::async(boost::launch::async, boost::bind(&StudyThreadClass::fab, this, 7));
 
    std::cout << result_7.get() << std::endl;
    std::cout << result_5.get() << std::endl;

    if (result_5.valid())
    {
        std::cout << result_5.get() << std::endl;
    }
    else {
        std::cout << "result_5 not valid." << std::endl;
    }
}
//结果:
21
8
result_5 not valid.

3. shared_future

        future曾在thread的早期版本⾥的名字是unique_future,这是因为它的结果只能使⽤get()获取 ⼀次,这个限制使它不能被多个线程并发访问,不够⽅便。 shared_future是future的增强版本,它与future类似,但它可以线程安全地多次调⽤get()获取计算结果。

        shared_future的接⼝与future相似,它们的区别仅在于get()的多线程访问性。 async()函数可以返回shared_future对象,但需要我们明确地声明类型。 future的share()函数也可以产⽣shared_future对象,这样就可以利⽤auto来⾃动推导类型,避免了⼿写的麻烦。

将上述代码改为:

auto result_5 = boost::async(boost::bind(&StudyThreadClass::fab, this, 5)).share();
//结果
21
8
8


 

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
提供的源码资源涵盖了小程序应用等多个领域,每个领域都包含了丰富的实例和项目。这些源码都是基于各自平台的最新技术和标准编写,确保了在对应环境下能够无缝运行。同时,源码中配备了详细的注释和文档,帮助用户快速理解代码结构和实现逻辑。 适用人群: 适合毕业设计、课程设计作业。这些源码资源特别适合大学生群体。无论你是计算机相关专业的学生,还是对其他领域编程感兴趣的学生,这些资源都能为你提供宝贵的学习和实践机会。通过学习和运行这些源码,你可以掌握各平台开发的基础知识,提升编程能力和项目实战经验。 使用场景及目标: 在学习阶段,你可以利用这些源码资源进行课程实践、课外项目或毕业设计。通过分析和运行源码,你将深入了解各平台开发的技术细节和最佳实践,逐步培养起自己的项目开发和问题解决能力。此外,在求职或创业过程中,具备跨平台开发能力的大学生将更具竞争力。 其他说明: 为了确保源码资源的可运行性和易用性,特别注意了以下几点:首先,每份源码都提供了详细的运行环境和依赖说明,确保用户能够轻松搭建起开发环境;其次,源码中的注释和文档都非常完善,方便用户快速上手和理解代码;最后,我会定期更新这些源码资源,以适应各平台技术的最新发展和市场需求。 所有源码均经过严格测试,可以直接运行,可以放心下载使用。有任何使用问题欢迎随时与博主沟通,第一时间进行解答!
C++网络编程和多线程是两个在软件开发中常见的主题。网络编程是指使用C++语言来创建网络应用程序,实现网络通信和数据传输。多线程是指在一个程序中同时执行多个线程,以提高程序的并发性和性能。 在C++中进行网络编程可以使用一些库,如Boost.Asio和Poco等。这些库提供了一些类和函数,用于创建网络连接、发送和接收数据等操作。通过这些库,可以实现各种网络应用,如客户端和服务器程序、网络通信协议的实现等。 多线程编程是指在一个程序中同时执行多个线程,每个线程可以独立执行不同的任务。C++提供了一些多线程编程的支持,如标准库中的std::thread和std::mutex等。使用这些库,可以创建和管理多个线程,并实现线程间的同步和通信。 在网络编程中,多线程可以用于处理多个客户端的请求。当有多个客户端同时连接到服务器时,可以为每个客户端创建一个线程来处理其请求,以提高服务器的并发性能。通过多线程,可以同时处理多个客户端的请求,而不会阻塞其他客户端的连接。 需要注意的是,在多线程编程中需要考虑线程安全性和同步机制。多个线程同时访问共享资源时,可能会引发竞态条件和数据不一致的问题。因此,需要使用互斥锁、条件变量等同步机制来保证线程的安全性和正确性。 总结起来,C++网络编程和多线程是两个在软件开发中常见的主题。网络编程用于创建网络应用程序,实现网络通信和数据传输。多线程用于提高程序的并发性和性能,特别适用于处理多个客户端的请求。在使用这两个主题时,需要注意线程安全性和同步机制的问题。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

烫青菜

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值