C++11 线程类(Thread)学习笔记
文档声明:
以下资料均属于本人在学习过程中产出的学习笔记,如果错误或者遗漏之处,请多多指正。并且该文档在后期会随着学习的深入不断补充完善。
资料仅供学习交流使用。
作者:Aliven888
1、简述
Thread:表示执行线程的类。
执行线程是指令序列,可以在多线程环境中与其他此类序列同时执行,同时共享相同的地址空间。
初始化的线程对象表示活动的执行线程;这样的线程对象是可连接的,并且具有唯一的线程ID。
默认构造(未初始化)的线程对象不可连接,并且其线程ID对于所有不可连接的线程都是通用的。
如果从中移出可连接线程,或者如果在它们上调用了join或detach,则该线程将不可连接。
2、接口描述
Member types
名称 | 描述 |
---|---|
id | 线程 id (public member type) |
native_handle_type | 本机句柄类型 (public member type) |
Member functions
名称 | 描述 |
---|---|
(constructor) | 构造函数 (public member function) |
(destructor) | 析构函数 (public member function) |
operator= | Move-assign thread (public member function) |
get_id | 获取线程 id (public member function) |
joinable | 检查是否可加入 (public member function) |
join | 加入线程 (public member function) |
detach | 分离线程 (public member function) |
swap | 交换两个线程 (public member function) |
native_handle | 获取本机句柄类型 (public member function) |
hardware_concurrency [static] | 检测硬件并发 (public static member function) |
3、接口介绍
3.1、成员类型(Member types)
3.1.1、Thread::id
默认构造的thread :: id对象的值标识不可连接的线程,因此,其值等于任何此类线程的成员thread :: get_id返回的值。
对于可连接线程,thread :: get_id返回此类型的唯一值,该值与其他任何可连接或不可连接线程返回的值不相等。
请注意,某些库实现可能会重新利用无法再加入的终止线程的thread :: id值。
3.1.2、Thread::native_handle_type
如果库实现支持,则该成员类型仅出现在类线程中。
它是thread :: native_handle返回的类型,具有有关线程的特定于实现的信息。
3.2、成员函数(Member functions)
3.2.1、构造函数(Thread::Thread(...))
默认构造函数:
Thread::thread() noexcept;
功能:构造一个不代表任何执行线程的线程对象。
初始化构造函数:
template <class Fn, class... Args>
explicit Thread::thread (Fn&& fn, Args&&... args);
功能:构造一个代表新的可连接执行线程的线程对象。新的执行线程调用fn传递args作为参数(使用其左值或右值引用的衰减副本)。该构造函数的完成与该fn副本的调用开始同步。
复制构造函数
Thread::thread (const thread&) = delete;
功能:删除构造函数形式(线程对象无法复制)。
移动构造函数
Thread::thread (thread&& x) noexcept;
功能:构造一个线程对象,该对象获取x表示的执行线程(如果有)。此操作不会以任何方式影响已移动线程的执行,它只是传输其处理程序。
x对象不再代表任何执行线程。
构造函数参量介绍:
fn: 指向函数的指针,指向成员的指针或任何类型的可移动构造的函数对象(即,其类定义了operator()的对象,包括闭包和函数对象)。返回值(如果有)将被忽略。
args ...: 传递给fn调用的参数(如果有)。它们的类型应是可移动构造的。如果fn是成员指针,则第一个参数应是为其定义了该成员的对象(或其引用或指向它的指针)。
X: 线程对象,其状态已移至构造对象。
注意: fn和args ...是模板参数;如果隐式推导,则它们是将参数绑定到的正确的左值或右值引用类型。但是请注意,在新线程中对fn的调用中,始终使用fn和args ...的衰减副本。
std::atomic<int> global_counter (0);
void increase_global (int n) { for (int i=0; i<n; ++i) ++global_counter; }
void increase_reference (std::atomic<int>& variable, int n) { for (int i=0; i<n; ++i) ++variable; }
struct C : std::atomic<int> {
C() : std::atomic<int>(0) {}
void increase_member (int n) { for (int i=0; i<n; ++i) fetch_add(1); }
};
int main ()
{
std::vector<std::thread> threads;
std::cout << "increase global counter with 10 threads...\n";
for (int i=1; i<=10; ++i)
threads.push_back(std::thread(increase_global,1000));
std::cout << "increase counter (foo) with 10 threads using reference...\n";
std::atomic<int> foo(0);
for (int i=1; i<=10; ++i)
threads.push_back(std::thread(increase_reference,std::ref(foo),1000));
std::cout << "increase counter (bar) with 10 threads using member...\n";
C bar;
for (int i=1; i<=10; ++i)
threads.push_back(std::thread(&C::increase_member,std::ref(bar),1000));
std::cout << "synchronizing all threads...\n";
for (auto& th : threads) th.join();
std::cout << "global_counter: " << global_counter << '\n';
std::cout << "foo: " << foo << '\n';
std::cout << "bar: " << bar << '\n';
return 0;
}
输出:
increase global counter using 10 threads...
increase counter (foo) with 10 threads using reference...
increase counter (bar) with 10 threads using member...
synchronizing all threads...
global_counter: 10000
foo: 10000
bar: 10000
3.2.2、析构函数(Thread::~Thread())
Thread::~Thread()
线程析构函数,常用于销毁线程对象。如果线程在销毁时是可连接的,则调用terminate()。
3.2.2、运算符重载(Thread::operator=)
常用接口有:
move (1)
thread& operator= (thread&& rhs) noexcept;
copy [deleted] (2)
thread& operator= (const thread&) = delete;
void pause_thread(int n)
{
std::this_thread::sleep_for (std::chrono::seconds(n));
std::cout << "pause of " << n << " seconds ended\n";
}
int main()
{
std::thread threads[5]; // default-constructed threads
std::cout << "Spawning 5 threads...\n";
for (int i=0; i<5; ++i)
threads[i] = std::thread(pause_thread,i+1); // move-assign threads
std::cout << "Done spawning threads. Now waiting for them to join:\n";
for (int i=0; i<5; ++i)
threads[i].join();
std::cout << "All threads joined!\n";
return 0;
}
输出:
Spawning 5 threads...
Done spawning threads. Now waiting for them to join:
pause of 1 seconds ended
pause of 2 seconds ended
pause of 3 seconds ended
pause of 4 seconds ended
pause of 5 seconds ended
All threads joined!
3.2.4、获取线程ID(Thread::get_id())
std::thread::id get_id() const noexcept;
功能: 获取线程ID
返回值: 如果线程对象是可连接的,则该函数返回一个唯一标识线程的值。如果线程对象不可连接,则该函数返回成员类型为thread :: id的默认构造的对象。
std::thread::id main_thread_id = std::this_thread::get_id();
void is_main_thread() {
if ( main_thread_id == std::this_thread::get_id() )
std::cout << "This is the main thread.\n";
else
std::cout << "This is not the main thread.\n";
}
int main()
{
is_main_thread();
std::thread th (is_main_thread);
th.join();
}
输出:
This is the main thread.
This is not the main thread.
3.2.5、检查是否可加入(Thread::joinable())
bool joinable() const noexcept;
功能: 检查是否可加入
返回值: 返回线程对象是否可连接。如果线程对象表示执行线程,则该对象是可连接的。如果线程可连接,则为true。否则为false。
注意事项:
在以下任何情况下,线程对象均不可连接:
如果它是默认构造的。
如果已将其移出(构造另一个线程对象或对其进行分配)。
是否已调用其成员join或detach。
void mythread()
{
// do stuff...
}
int main()
{
std::thread foo;
std::thread bar(mythread);
std::cout << "Joinable after construction:\n" << std::boolalpha;
std::cout << "foo: " << foo.joinable() << '\n';
std::cout << "bar: " << bar.joinable() << '\n';
if (foo.joinable()) foo.join();
if (bar.joinable()) bar.join();
std::cout << "Joinable after joining:\n" << std::boolalpha;
std::cout << "foo: " << foo.joinable() << '\n';
std::cout << "bar: " << bar.joinable() << '\n';
return 0;
}
输出:
Joinable after construction:
foo: false
bar: true
Joinable after joining:
foo: false
bar: false
3.2.6、连接线程(Thread::join())
void join();
功能:
这使该函数返回的时刻与线程中所有操作的完成同步。
阻塞调用该函数的线程的执行,直到构造函数上调用的函数返回为止(函数阻塞,直到线程执行完毕)。
线程执行完成后,该函数返回。
调用此函数后,线程对象变得不可连接,并且被安全地销毁。
返回值: void
void pause_thread(int n)
{
std::this_thread::sleep_for (std::chrono::seconds(n));
std::cout << "pause of " << n << " seconds ended\n";
}
int main()
{
std::cout << "Spawning 3 threads...\n";
std::thread t1 (pause_thread,1);
std::thread t2 (pause_thread,2);
std::thread t3 (pause_thread,3);
std::cout << "Done spawning threads. Now waiting for them to join:\n";
t1.join();
t2.join();
t3.join();
std::cout << "All threads joined!\n";
return 0;
}
输出:
Spawning 3 threads...
Done spawning threads. Now waiting for them to join:
pause of 1 seconds ended
pause of 2 seconds ended
pause of 3 seconds ended
All threads joined!
3.2.7、分离线程(Thread::detach())
void detach();
功能:
从调用线程中分离对象所代表的线程,从而使它们彼此独立执行。
两个线程继续运行,而不会阻塞或以任何方式进行同步。 请注意,当任何一个执行结束时,其资源将被释放。
调用此函数后,线程对象变得不可连接,并且可以安全地被销毁。
返回值: void
void pause_thread(int n)
{
std::this_thread::sleep_for (std::chrono::seconds(n));
std::cout << "pause of " << n << " seconds ended\n";
}
int main()
{
std::cout << "Spawning and detaching 3 threads...\n";
std::thread (pause_thread,1).detach();
std::thread (pause_thread,2).detach();
std::thread (pause_thread,3).detach();
std::cout << "Done spawning threads.\n";
std::cout << "(the main thread will now pause for 5 seconds)\n";
// give the detached threads time to finish (but not guaranteed!):
pause_thread(5);
return 0;
}
输出:
Spawning and detaching 3 threads...
Done spawning threads.
(the main thread will now pause for 5 seconds)
pause of 1 seconds ended
pause of 2 seconds ended
pause of 3 seconds ended
pause of 5 seconds ended
3.2.8、(Thread::swap())
void swap (thread& x) noexcept;
功能: 用x线程对象交换当前线程对象的状态。x线程对象和当前线程对象均被修改。
返回值: void
3.2.9、(Thread::native_handle())
native_handle_type native_handle();
功能:
获取本地句柄;如果库实现支持,则该成员函数仅存在于类线程中。如果存在,它将返回一个值,该值用于访问与线程关联的特定于实现的信息。
3.2.10、(Thread::hardware_concurrency ())
static unsigned hardware_concurrency() noexcept;
功能: 检测硬件并发;返回硬件线程上下文的数量。
此值的解释是特定于系统和实现的,可能不准确,而只是一个近似值。
请注意,这不需要与系统中可用的处理器或内核的实际数量相匹配:系统可以为每个处理单元支持多个线程,或限制对程序的资源访问。
如果此值不可计算或定义不正确,则该函数返回0。