C++11多线程:thread头文件

std:thread

  • 1. std::thread(class)
    • 线程构造(constructor)
    • joinable
    • 线程状态
    • operator=
    • join
    • detach
    • get_id
    • native_handle
    • swap
    • hardware_concurrency
  • 2. std::this_thread(namespace)
    • get_id
    • sleep_for
    • sleep_until
    • yield

#include头文件中主要包含两个内容:std:thread类和std:this_thread命名空间。

1. std::thread(class)

生成单个可执行线程,可以在多线程环境中与其他线程并发执行,同时共享相同的地址空间。初始化的线程对象拥有唯一的线程id,状态为joinable;未初始化的线程对象状态为non-joinabe,与其他non-joinable的线程对象共用通用的线程id。线程对象从joinable到non-joinable有以下两种途径:1)通过move被转移; 2)线程调用join或者detach函数。

C++11多线程:thread头文件_第1张图片

线程构造(constructor)

C++11多线程:thread头文件_第2张图片
(1). 默认构造,创建一个空的thread对象,状态non-joinable。
(2). 初始化构造,传递功能函数fn和参数args,状态joinable。创建成功线程立即启动。
(3). 拷贝构造函数(被禁用),意味着 thread 不可被拷贝构造。
(4). 移动构造,传递线程x的句柄。构造成功后x置空,等同于(1)。

注意:处于joinable状态的线程对象必须调用join或者detach函数后才能被销毁。

//参数传递
#include  
using namespace std;  

void fun1(int n)  
{  
    //do something... 
}  
void fun2(int & n)
{  
    //do something...
}  
int main()  
{  
    int n = 0;  
    thread t1;               //默认构造
    thread t2(fun1, n);      //初始化构造(值传递)
    t2.join();  
    n = 10;  
    thread t3(fun2, ref(n)); //初始化构造(引用传递)
    thread t4(move(t3));     //移动构造(t4是执行线程,t3变空线程,等同于t1)
    t4.join();  
    return 0;  
}

joinable

bool joinable() const noexcept; // 判断线程对象是否joinable

joinable:

  • 正在执行的线程

non-joinable:

  • 默认构造
  • 所有权被转移(移动构造)
  • 调用了join或者detach

线程状态

无论在windows中还是Posix中,主线程和子线程的默认关系是:无论子线程执行完毕与否,一旦主线程执行完毕退出,所有子线程执行都会终止。这时整个进程结束或僵死,部分线程保持一种终止执行但还未销毁的状态,而进程必须在其所有线程销毁后销毁,这时进程处于僵死状态。线程函数执行完毕退出,或以其他非常方式终止,线程进入终止态,但是为线程分配的系统资源不一定释放,可能在系统重启之前,一直都不能释放,终止态的线程,仍旧作为一个线程实体存在于操作系统中,什么时候销毁,取决于线程属性。在这种情况下,主线程和子线程通常定义以下两种关系:

1、可会合(joinable):这种关系下,主线程需要明确执行等待操作,在子线程结束后,主线程的等待操作执行完毕,子线程和主线程会合,这时主线程继续执行等待操作之后的下一步操作。主线程必须会合可会合的子线程。在主线程的线程函数内部调用子线程对象的wait函数实现,即使子线程能够在主线程之前执行完毕,进入终止态,也必须执行会合操作,否则,系统永远不会主动销毁线程,分配给该线程的系统资源也永远不会释放。

2、相分离(detached):表示子线程无需和主线程会合,也就是相分离的,这种情况下,子线程一旦进入终止状态,系统立即销毁线程,回收资源。这种方式常用在线程数较多的情况下,有时让主线程逐个等待子线程结束,或者让主线程安排每个子线程结束的等待顺序,是很困难或不可能的,所以在并发子线程较多的情况下,这种方式也会经常使用。

在任何一个时间点上,线程是可结合(joinable)或者是可分离的(detached),一个可结合的线程能够被其他线程回收资源和杀死,在被其他线程回收之前,它的存储器资源如栈,是不释放的,相反,一个分离的线程是不能被其他线程回收或杀死的,它的存储器资源在它终止时由系统自动释放。

线程的分离状态决定一个线程以什么样的方式来终止自己,在默认的情况下,线程是非分离状态的,这种情况下,原有的线程等待创建的线程结束,只有当pthread_join函数返回时,创建的线程才算终止,释放自己占用的系统资源,而分离线程没有被其他的线程所等待,自己运行结束了,线程也就终止了,马上释放系统资源。

operator=

在这里插入图片描述
(1). 如果线程对象现在是non-joinable,则其获取由右值引用(rhs)表示的执行线程。如果是joinable,则调用terminate(),默认终止线程。右值传递之后,不再表示任何执行线程。
(2). 线程对象无法被复制(线程对象是可转移的,不可复制的

// example for thread::operator=
#include        // std::cout
#include          // std::thread, std::this_thread::sleep_for
#include          // std::chrono::seconds
 
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;
}

join

void join(); //将子线程加入汇合队列
  • 该函数的返回与子线程执行完毕同步,该函数会阻塞调用该函数的线程直到子线程调用完毕。(即等待子线程执行完毕才继续执行主线程)
  • 调用join后,线程对象由joinable状态进入non-joinable状态,可以被安全销毁。

detach

void detach(); //分离线程
  • 将子线程与调用线程分离,彼此独立执行,调用线程不会发生阻塞,子线程执行完自动退出。
  • 线程间没有以任何方式同步,资源独立释放。
  • 调用detach后,线程对象由joinable状态进入non-joinable状态,可以被安全销毁。

detach与join最大的区别在于:线程调用join后被阻塞,等到子线程运行完毕汇合后再继续后续操作,最后一起释放资源。detach不需要阻塞和汇合,调用线程和子线程完全分离,资源独立释放,可能出现调用线程结束了子线程还在后台运行的情况。

get_id

id get_id() const noexcept; //获取线程id
  • 如果thread对象joinable,则返回执行线程的唯一id
  • 如果thread对象non-joinable,则返回公用的默认构造对象的id

native_handle

native_handle_type native_handle(); //返回当前线程句柄

swap

void swap (thread& x) noexcept; //交换两个线程的状态信息,比如线程id
void swap (thread& x, thread& y) noexcept; //交换线程x和y的状态
//该方法是函数swap的重载,效果类似于x.swap(y)。

hardware_concurrency

static unsigned hardware_concurrency() noexcept; //返回硬件线程上下文数量

此处并非返回系统可用处理器或者内核的实际数量,因为每个处理单元可以支持多个线程。只能返回一个硬件线程数量的近似值,如果该值无法计算或者没有很好地定义,则返回0.

2. std::this_thread(namespace)

该命名空间提供当前线程的一些访问函数:
C++11多线程:thread头文件_第3张图片

get_id

thread::id get_id() noexcept; //返回当前线程的id号

sleep_for

template <class Rep, class Period>
void sleep_for (const chrono::duration<Rep,Period>& rel_time); //线程休眠一段时间

当前线程休眠rel_time长的时间,其他线程照常运行。示例:std::this_thread::sleep_for (std::chrono::seconds(1));

sleep_until

template <class Clock, class Duration>
void sleep_until (const chrono::time_point<Clock,Duration>& abs_time); //线程休眠到某个时间点

当前线程休眠到时间点abs_time,其他线程照常运行。示例:std::this_thread::sleep_until (system_clock::from_time_t (mktime(ptm)));

yield

void yield() noexcept; //当前线程让步给其他线程执行

重新调度线程的实现,其他线程优先执行,当前线程等待(注意:此时未休眠)

// this_thread::yield example
#include        // std::cout
#include          // std::thread, std::this_thread::yield
#include          // std::atomic

std::atomic<bool> ready (false);

void count1m(int id) {
  while (!ready) {             // wait until main() sets ready...
    std::this_thread::yield();
  }
  for (volatile int i=0; i<1000000; ++i) {}
  std::cout << id;
}

int main ()
{
  std::thread threads[10];
  std::cout << "race of 10 threads that count to 1 million:\n";
  for (int i=0; i<10; ++i) threads[i]=std::thread(count1m,i);
  ready = true;               // go!
  for (auto& th : threads) th.join();
  std::cout << '\n';

  return 0;
}

输出:

race of 10 threads that count to 1 million...
6189370542

你可能感兴趣的:(c++,多线程,面试,thread)