C++ std::thread join()的理解
https://www.cnblogs.com/adorkable/p/12722209.html
总结理解一下就是两个关键点:
谁调用了这个函数?
调用了这个函数的线程对象,一定要等这个线程对象的方法(在构造时传入的方法)执行完毕后(或者理解为这个线程的活干完了!),这个join()函数才能得到返回。
在什么线程环境下调用了这个函数?
必须要等线程方法执行完毕后才能返回,那必然是阻塞调用线程的,也就是说如果一个线程对象在一个线程环境调用了这个函数,那么这个线程环境就会被阻塞,直到这个线程对象在构造时传入的方法执行完毕后,才能继续往下走,另外如果线程对象在调用join()函数之前,就已经做完了自己的事情(在构造时传入的方法执行完毕),那么这个函数不会阻塞线程环境,线程环境正常执行。
C++11 std::thread detach()与join()用法总结
两者区别
在声明一个std::thread对象之后,都可以使用detach和join函数来启动被调线程, 区别在于两者是否阻塞主调线程。
(1)当使用join()函数时,主调线程阻塞,等待被调线程终止,然后主调线程回收被调线程资源,并继续运行;
(2)当使用detach()函数时,主调线程继续运行,被调线程驻留后台运行,主调线程无法再取得该被调线程的控制权。当主调线程结束时,由运行时库负责清理与被调线程相关的资源。
detach()
detach调用之后,目标线程就成为了守护线程,驻留后台运行,与之关联的std::thread对象失去对目标线程的关联,无法再通过std::thread对象取得该线程的控制权。当线程主函数执行完之后,线程就结束了,运行时库负责清理与该线程相关的资源。
当一个thread对象到达生命期终点而关联线程还没有结束时,则thread对象取消与线程之间的关联,目标线程线程则变为分离线程继续运行。
当调用join函数时,调用线程阻塞等待目标线程终止,然后回收目标线程的资源。
detach是使主线程不用等待子线程可以继续往下执行,但即使主线程终止了,子线程也不一定终止。
#include <iostream>
#include <chrono>
#include <thread>
void independentThread()
{
std::cout << "Starting concurrent thread.\n";
std::this_thread::sleep_for(std::chrono::seconds(2));
std::cout << "Exiting concurrent thread.\n";
}
void threadCaller()
{
std::cout << "Starting thread caller.\n";
std::thread t(independentThread);
t.detach();
//t.join();
std::this_thread::sleep_for(std::chrono::seconds(1));
std::cout << "Exiting thread caller.\n";
}
int main()
{
threadCaller();
std::this_thread::sleep_for(std::chrono::seconds(5));
}
get_id: 获取线程 ID。
yield: 当前线程放弃执行,操作系统调度另一线程继续执行。
sleep_until: 线程休眠至某个指定的时刻(time point),该线程才被重新唤醒。
sleep_for: 线程休眠某个指定的时间片(time span),该线程才被重新唤醒,不过由于线程调度等原因,实际休眠时间可能比
sleep_duration 所表示的时间片更长。
get_id()
std::thread t(independentThread);
std::thread::id t1_id = t.get_id();
std::cout << t1_id <<".\n";
输出:140686121973504.
hardware_concurrency并发数
返回实现支持的并发线程数。应该只把该值当做提示。
#include <iostream>
#include <thread>
int main() {
unsigned int n = std::thread::hardware_concurrency();
std::cout << n << " concurrent threads are supported.\n";
}
输出:4 concurrent threads are supported.
pthread_create函数
linux下用C语言开发多线程程序,Linux系统下的多线程遵循POSIX线程接口,称为pthread。
int pthread_create(pthread_t *tidp, const pthread_attr_t *attr,( void *)(*start_rtn)( void *), void *arg);
参数
第一个参数为指向线程标识符的指针。
第二个参数用来设置线程属性。
第三个参数是线程运行函数的起始地址。
最后一个参数是运行函数的参数。
另外,在编译时注意加上-lpthread参数,以调用静态链接库。因为pthread并非Linux系统的默认库
#include <pthread.h>
#include <queue>
#include <stdio.h>
#include <unistd.h>
// 注意pthread_*函数返回的异常值
pthread_mutex_t mutex;
pthread_cond_t condvar;
std::queue<int> msgQueue;
struct Produce_range {
int start;
int end;
};
void *producer(void *args)
{
int start = static_cast<Produce_range *>(args)->start;
int end = static_cast<Produce_range *>(args)->end;
for (int x = start; x < end; x++) {
usleep(200 * 1000);
pthread_mutex_lock(&mutex);
msgQueue.push(x);
pthread_mutex_unlock(&mutex);
pthread_cond_signal(&condvar);
printf("Produce message %d\n", x);
}
pthread_exit((void *)0);
return NULL;
}
void *consumer(void *args)
{
int demand = *static_cast<int *>(args);
while (true) {
pthread_mutex_lock(&mutex);
while (msgQueue.size() <= 0) {
pthread_cond_wait(&condvar, &mutex);
}
if (msgQueue.size() > 0) {
printf("Consume message %d\n", msgQueue.front());
msgQueue.pop();
--demand;
}
pthread_mutex_unlock(&mutex);
if (!demand) break;
}
pthread_exit((void *)0);
return NULL;
}
int main()
{
pthread_attr_t attr;
pthread_attr_init(&attr);
pthread_mutex_init(&mutex, NULL);
pthread_cond_init(&condvar, NULL);
pthread_t producer1, producer2, producer3, consumer1, consumer2;
Produce_range range1 = {0, 10};
pthread_create(&producer1, &attr, producer, static_cast<void *>(&range1));
Produce_range range2 = {range1.end, range1.end + 10};
pthread_create(&producer2, &attr, producer, static_cast<void *>(&range2));
Produce_range range3 = {range2.end, range2.end + 10};
pthread_create(&producer3, &attr, producer, static_cast<void *>(&range3));
int consume_demand1 = 20;
int consume_demand2 = 10;
pthread_create(&consumer1, &attr, consumer,
static_cast<void *>(&consume_demand1));
pthread_create(&consumer2, &attr, consumer,
static_cast<void *>(&consume_demand2));
pthread_join(producer1, NULL);
pthread_join(producer2, NULL);
pthread_join(producer3, NULL);
pthread_join(consumer1, NULL);
pthread_join(consumer2, NULL);
}
std::chrono时间库及应用
(1)chrono命名空间定义好的时间单位
typedef duration <Rep, ratio<3600,1>> hours;
typedef duration <Rep, ratio<60,1>> minutes;
typedef duration <Rep, ratio<1,1>> seconds;
typedef duration <Rep, ratio<1,1000>> milliseconds;
typedef duration <Rep, ratio<1,1000000>> microseconds;
typedef duration <Rep, ratio<1,1000000000>> nanoseconds;
使用举例:
chrono::minutes mintu{2};//2分钟
chrono::seconds sec{3};//3秒钟
chrono::milliseconds mills{500};//500毫秒
auto dul = sec - mills;//两者差值,单位默认转到更小的 2500ms
dul.count(); //值为2500
std::this_thread::sleep_for(std::chrono::milliseconds(100)); //当前线程休眠100毫秒
//chrono::duration_cast<>() 时间单位转换
chrono::duration_cast<chrono::seconds>(mintu).count(); //2分钟换算为120秒
(2) 获取当前时间(time_point 表示一个时间点)
获取当前时间
chrono::system_clock::time_point now = chrono::system_clock::now();//当前时间time_point格式
std::time_t oldTime = time(nullptr);//c函数获取当前时间
cout << "oldTime = " << oldTime << endl;
chrono::system_clock::time_point timePoint = chrono::system_clock::now();//stl库获取当前时间
std::time_t newTime = chrono::system_clock::to_time_t(timePoint);//转换为旧式接口,单位:秒
cout<<"newTime = " << newTime <<endl;// oldTime == timeT
格式化打印当前时间
/* chrono::system_clock::time_point与std::time_t类型可相互函数
* chrono::system_clock::to_time_t()
* chrono::system_clock::from_time_t()
*/
std::time_t nowTime = chrono::system_clock::to_time_t(now);//转换为 std::time_t 格式
std::put_time(std::localtime(&nowTime), "%Y-%m-%d %X"); // 2019-06-18 14:25:56
// !std::localtime非线程安全,使用localtime_r函数代替
struct tm cutTm = {0};
std::put_time(localtime_r(&nowTime, &cutTm), "%Y-%m-%d %X");// 2019-06-18 14:25:56
(3)打印程序耗时
/*1、打印耗时,取变量构造函数与析构函数的时间差,单位ms*/
class SpendTime
{
public:
SpendTime():_curTimePoint(std::chrono::steady_clock::now())
{
}
~SpendTime()
{
auto curTime = std::chrono::steady_clock::now();
auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(curTime -
_curTimePoint);
cout<<"SpendTime = "<< duration.count() <<"ms"<<endl;
}
private:
std::chrono::steady_clock::time_point _curTimePoint;
};