使用 C++11 编写 Linux 多线程程序(3)

线程类 std::thread 的其它方法和特点

thread 类是一个特殊的类,它不能被拷贝,只能被转移或者互换,这是符合线程的语义的,不要忘记这里所说的线程是直接被操作系统调度的。线程的转移使用 move 函数,示例如下:

清单 6.例子 thread_move.cc

void threadMove(void){ int a = 1; thread t( [](int* pa){ for(;;){ *pa = (*pa * 33) % 0x7fffffff; if ( ( (*pa) >> 30) & 1) break; } }, &a); thread t2 = move(t); // 改为 t2 = t 将不能编译。 t2.join(); cout << "a=" << a << endl; }

在这个例子中,如果将 t2.join() 改为 t.join() 将会导致整个进程被结束,因为忘记了调用 t2 也就是被转移的线程的 join() 方法,从而导致整个进程被结束,而 t 则因为已经被转移,其 id 已被置空。

线程实例互换使用 swap 函数,示例如下:

清单 7.例子 thread_swap.cc

void threadSwap(void){ int a = 1; thread t( [](int* pa){ for(;;){ *pa = (*pa * 33) % 0x7fffffff; if ( ( (*pa) >> 30) & 1) break; } }, &a); thread t2; cout << "before swap: t=" << t.get_id() << ", t2=" << t2.get_id() << endl; swap(t, t2); cout << "after swap : t=" << t.get_id() << ", t2=" << t2.get_id() << endl; t2.join(); cout << "a=" << a << endl; }

互换和转移很类似,但是互换仅仅进行实例(以 id 作标识)的互换,而转移则在进行实例标识的互换之前,还进行了转移目的实例(如下例的t2)的清理,如果 t2 是可聚合的(joinable() 方法返回 true),则调用 std::terminate(),这会导致整个进程退出,比如下面这个例子:

清单 8.例子 thread_move_term.cc

void threadMoveTerm(void){ int a = 1; thread t( [](int* pa){ for(;;){ *pa = (*pa * 33) % 0x7fffffff; if ( ( (*pa) >> 30) & 1) break; } }, &a); thread t2( [](){ int i = 0; for(;;)i++; } ); t2 = move(t); // 将会导致 std::terminate() cout << "should not reach here" << endl; t2.join(); }

所以,在进行线程实例转移的时候,要注意判断目的实例的 id 是否为空值(即 id())。

如果我们继承了 thread 类,则还需要禁止拷贝构造函数、拷贝赋值函数以及赋值操作符重载函数等,另外,thread 类的析构函数并不是虚析构函数。示例如下:

清单 9.例子 thread_inherit.cc

class MyThread : public thread{ public: MyThread() noexcept : thread(){}; template<typename Callable, typename... Args> explicit MyThread(Callable&& func, Args&&... args) : thread( std::forward<Callable>(func), std::forward<Args>(args)...){ } ~MyThread() { thread::~thread(); } // disable copy constructors MyThread( MyThread& ) = delete; MyThread( const MyThread& ) = delete; MyThread& operator=(const MyThread&) = delete; };

因为 thread 类的析构函数不是虚析构函数,在上例中,需要避免出现下面这种情况:

MyThread* tc = new MyThread(...);

...

thread* tp = tc;

...

delete tp;

这种情况会导致 MyThread 的析构函数没有被调用。

linux

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/5fcf05c7c23c8234da980e853401e877.html