龙空技术网

C++ 多线程编程系列 | std:thread 到底是什么

编码有术 362

前言:

如今小伙伴们对“c语言beginthread”大致比较注重,各位老铁们都需要分析一些“c语言beginthread”的相关资讯。那么小编在网络上网罗了一些关于“c语言beginthread””的相关资讯,希望姐妹们能喜欢,小伙伴们一起来了解一下吧!

1、std::thread (C++11)

std::thread 表示一个线程,在构造时开始线程执行。

1.1、基本使用

std::thread 默认构造函数 (1) 不会创建任何线程;拷贝构造函数 (4) 是删除的,不允许通过拷贝创建一个 std::thread 对象;但是移动构造函数是可以使用的,other 在移动后不代表一个线程。

thread() noexcept; // (1)thread( thread&& other ) noexcept; // (2)template< class Function, class... Args >explicit thread( Function&& f, Args&&... args ); // (3)thread( const thread& ) = delete; // (4)

构造函数 (3) 接受一个可调用对象 f,以及传递参数给 f。函数参数是模板参数右值引用类型,可以接受左值和右值。

#include <iostream>#include <thread>class Foo { public:  static void Bar1(const char* msg) {    std::cout << "Bar1 executing: " << msg << std::endl;  }  void Bar2(const char* msg) {    std::cout << "Bar2 executing: " << msg << std::endl;  }};int main() {  Foo foo;  std::thread t1(Foo::Bar1, "thread1");  std::thread t2(&Foo::Bar2, &foo, "thread2");  t1.join();  t2.join();  return 0;}

当线程构造完成,可以调用 joinable() 判断是否正在执行,或调用 join() 等待线程执行结束,也可以调用 detach() 让线程后台执行,用户需要确保线程后台执行时,访问的资源不会被释放。

jion() 和 detach() 调用返回后,joinable() 返回 false。

#include <stdio.h>#include <chrono>#include <thread>class Foo { public:  static void Bar1(const char* msg) {    for (int i = 0; i < 5; ++i) {      printf("%s executing... #%d\n", msg, i);      std::this_thread::sleep_for(std::chrono::milliseconds(50));    }  }  static void Bar2(const char* msg) {    printf("%s begin\n", msg);    std::thread bg(Foo::Bar1, "bgthread");    bg.detach();    printf("bg joinable %d\n", bg.joinable());    printf("%s end\n", msg);  }};int main() {  std::thread t1(Foo::Bar2, "thread1");  printf("t1 joinable %d\n", t1.joinable());  t1.join();  printf("t1 joinable %d\n", t1.joinable());  std::this_thread::sleep_for(std::chrono::seconds(1));  return 0;}

输出为

t1 joinable 1thread1 beginbg joinable 0thread1 endbgthread executing... #0t1 joinable 0bgthread executing... #1bgthread executing... #2bgthread executing... #3bgthread executing... #4

std::thread 还支持赋值操作,但是 other 必须时右值。如果 *this 没有绑定到线程,就将 other 的状态赋值给 *this,否则 std::terminate() 将会调用。

thread& operator=( thread&& other ) noexcept;

比如下面的例子,t1 使用默认构造函数,没有绑定任何线程,t2 可以转移给 t1。当 t3 已经绑定线程,正在执行,将 t4 绑定到 t3 时,会出现错误。

#include <stdio.h>#include <chrono>#include <thread>class Foo { public:  static void Bar1(const char* msg) {    for (int i = 0; i < 5; ++i) {      printf("%s executing... #%d\n", msg, i);      std::this_thread::sleep_for(std::chrono::milliseconds(50));    }  }};int main() {  std::thread t1;  std::thread t2(Foo::Bar1, "thread1");  t1 = std::move(t2);  printf("t2 joinable %d\n", t2.joinable());  t1.join();  std::thread t3(Foo::Bar1, "thread3");  std::thread t4(Foo::Bar1, "thread4");  t3 = std::move(t4);  t3.join();  return 0;}

输出结果为

t2 joinable 0thread1 executing... #0thread1 executing... #1thread1 executing... #2thread1 executing... #3thread1 executing... #4thread3 executing... #0terminate called without an active exceptionthread4 executing... #0Aborted

最后,可以使用 get_id() 返回线程 id,标记一个 id。如果使用赋值转移 thread 给所有权,id 也是会转移的。

std::thread::id get_id() const noexcept;

也可以使用 std::this_thread::get_id() 来获取当前线程的 id。另外std::this_thread 提供 yeild/sleep_for/sleep_until() 等函数,可以主动让出 CPU,让其他线程使用 CPU,操作系统再次调度获取 CPU 后继续执行。

1.2、底层实现1.2.1、std::thread::id

thread::id 有一个数据成员 _M_thread,Linux 系统中,就是 pthread_t

/// std_thread.h    class id    {      native_handle_type	_M_thread;    public:      id() noexcept : _M_thread() { }      explicit      id(native_handle_type __id) : _M_thread(__id) { }    private://...    };
1.2.2、std::thread::_State

_State 用于封装一个 functor,创建线程后,线程调用 _M_run() 函数。_State 是一个虚基类,_State_impl 是其实现类。

_State_impl 只有一个 _Callable 类型的成员变量 _M_func,其包装了调用参数,可以直接调用执行 _M_func()

/// std_thread.h    struct _State    {      virtual ~_State();      virtual void _M_run() = 0;    };    using _State_ptr = unique_ptr<_State>;/// impl    template<typename _Callable>      struct _State_impl : public _State      {    _Callable		_M_func;    template<typename... _Args>      _State_impl(_Args&&... __args)      : _M_func{{std::forward<_Args>(__args)...}}      { }    void    _M_run() { _M_func(); }      };

_State_impl 模板函数 _Callable 是 _Invoker 类。_Invoker 重载了函数调用符 (),调用 std::__invoke() 函数,触发传入的 functor 以及参数。

/// std_thread.h    template<typename _Tuple>      struct _Invoker      {    _Tuple _M_t;    template<typename>      struct __result;    template<typename _Fn, typename... _Args>      struct __result<tuple<_Fn, _Args...>>      : __invoke_result<_Fn, _Args...>      { };    template<size_t... _Ind>      typename __result<_Tuple>::type      _M_invoke(_Index_tuple<_Ind...>)      { return std::__invoke(std::get<_Ind>(std::move(_M_t))...); }    typename __result<_Tuple>::type    operator()()    {      using _Indices        = typename _Build_index_tuple<tuple_size<_Tuple>::value>::__type;      return _M_invoke(_Indices());    }      };      using _Call_wrapper = _Invoker<tuple<typename decay<_Tp>::type...>>;
1.2.3、std::thread

std::thread 一个成员变量,_M_id,表示线程 id

/// std_thread.h  class thread  {  public:    using native_handle_type = __gthread_t;  private:    id				_M_id;// ...  };

当传入 functor 和参数时,调用如下构造函数,然后调用 _M_start_thread() 函数。

/// std_thread.h#ifdef _GLIBCXX_HAS_GTHREADS    template<typename _Callable, typename... _Args,         typename = _Require<__not_same<_Callable>>>      explicit      thread(_Callable&& __f, _Args&&... __args)      {    static_assert( __is_invocable<typename decay<_Callable>::type,                      typename decay<_Args>::type...>::value,      "std::thread arguments must be invocable after conversion to rvalues"      );#ifdef GTHR_ACTIVE_PROXY    // Create a reference to pthread_create, not just the gthr weak symbol.    auto __depend = reinterpret_cast<void(*)()>(&pthread_create);#else    auto __depend = nullptr;#endif    using _Wrapper = _Call_wrapper<_Callable, _Args...>;    // Create a call wrapper with DECAY_COPY(__f) as its target object    // and DECAY_COPY(__args)... as its bound argument entities.    _M_start_thread(_State_ptr(new _State_impl<_Wrapper>(          std::forward<_Callable>(__f), std::forward<_Args>(__args)...)),        __depend);      }#endif // _GLIBCXX_HAS_GTHREADS

_M_start_thread() 函数利用操作系统接口创建一个线程,线程执行 execute_native_thread_routine() 函数

/// thread.cc  void  thread::_M_start_thread(_State_ptr state, void (*)())  {    if (!__gthread_active_p())      {#if __cpp_exceptions    throw system_error(make_error_code(errc::operation_not_permitted),               "Enable multithreading to use std::thread");#else    __builtin_abort();#endif      }    const int err = __gthread_create(&_M_id._M_thread,                     &execute_native_thread_routine,                     state.get());    if (err)      __throw_system_error(err);    state.release();  }

execute_native_thread_routine() 其实就是调用 _State_impl::_M_run() 函数,触发用于指定的 functor 以及 args。

/// thread.c    static void*    execute_native_thread_routine(void* __p)    {      thread::_State_ptr __t{ static_cast<thread::_State*>(__p) };      __t->_M_run();      return nullptr;    }

join() 和 detach() 函数也是分别调用操作系统函数执行,最后将 _M_id 赋值为 id(),表示 std::thread 已经不绑定到任何线程。

/// thread.cc  void  thread::join()  {    int __e = EINVAL;    if (_M_id != id())      __e = __gthread_join(_M_id._M_thread, 0);    if (__e)      __throw_system_error(__e);    _M_id = id();  }  void  thread::detach()  {    int __e = EINVAL;    if (_M_id != id())      __e = __gthread_detach(_M_id._M_thread);    if (__e)      __throw_system_error(__e);    _M_id = id();  }
1.2.4、std::this_thread

this_thread 并不是一个类,而是命名空间。get_id() 和 yield() 函数分别调用 pthread_self() 和 sched_yield(),后者 sched_yield() 是 Linux 系统调用。

/// std_thread.hnamespace this_thread  {    inline thread::id    get_id() noexcept    {#ifndef _GLIBCXX_HAS_GTHREADS// ...#elif defined _GLIBCXX_NATIVE_THREAD_ID      return thread::id(_GLIBCXX_NATIVE_THREAD_ID); // pthread_self()#else// ...#endif    }    inline void    yield() noexcept    {#if defined _GLIBCXX_HAS_GTHREADS && defined _GLIBCXX_USE_SCHED_YIELD      __gthread_yield(); // sched_yield()#endif    }  } // namespace this_thread

sleep_for() 函数底层调用 nanosleep() 函数 (libc),时间粒度是纳秒级。sleep_until() 函数是借助 sleep_for() 函数实现的。

/// this_thread_sleep.h  namespace this_thread  {    /// this_thread::sleep_for    template<typename _Rep, typename _Period>      inline void      sleep_for(const chrono::duration<_Rep, _Period>& __rtime)      {    if (__rtime <= __rtime.zero())      return;    auto __s = chrono::duration_cast<chrono::seconds>(__rtime);    auto __ns = chrono::duration_cast<chrono::nanoseconds>(__rtime - __s);#ifdef _GLIBCXX_USE_NANOSLEEP    struct ::timespec __ts =      {        static_cast<std::time_t>(__s.count()),        static_cast<long>(__ns.count())      };    while (::nanosleep(&__ts, &__ts) == -1 && errno == EINTR)      { }#else// ...#endif      }    /// this_thread::sleep_until    template<typename _Clock, typename _Duration>      inline void      sleep_until(const chrono::time_point<_Clock, _Duration>& __atime)      {#if __cplusplus > 201703L    static_assert(chrono::is_clock_v<_Clock>);#endif    auto __now = _Clock::now();    if (_Clock::is_steady)      {        if (__now < __atime)          sleep_for(__atime - __now);        return;      }    while (__now < __atime)      {        sleep_for(__atime - __now);        __now = _Clock::now();      }      }  } // namespace this_thread

标签: #c语言beginthread