欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 新闻 > 社会 > C++11标准(4)——并发库(多线程)

C++11标准(4)——并发库(多线程)

2025/6/21 6:39:10 来源:https://blog.csdn.net/2301_77239666/article/details/148382285  浏览:    关键词:C++11标准(4)——并发库(多线程)

欢迎来到博主的专栏:c++杂谈
博主ID:代码小豪

文章目录

    • thread的相关函数
    • thisthread

c++11新增了与并发相关的库,包含线程、以及互斥、同步等与线程安全相关的库,与linux中所使用POSIX库不同,并发库是将其进行了封装,不再是面向过程的使用方式,并且添加了一些c++11的特性,比如右值引用,可变参数模板等。

那么这么做有什么好处呢?第一使用并发库可以跨平台,比如在linux环境下,并发管理使用的pthread相关的函数,在windows下则是Thread。而c++并发库底层是对各个系统的线程库进行封装,所以不同的环境使用并发库的方法是一致的。

下面是linux和windows的线程创建的差异对比

//LINUX pthread库创建线程int pthread_create(pthread_t *tidp,const pthread_attr_t *attr, void *(*start_rtn)(void*), void *arg);// windows线程创建API 
HANDLE CreateThread(LPSECURITY_ATTRIBUTES lpThreadAttributes,//SDSIZE_T dwStackSize,//initialstacksizeLPTHREAD_START_ROUTINE lpStartAddress,//threadfunctionLPVOID lpParameter,//threadargumentDWORD dwCreationFlags,//creationoptionLPDWORD lpThreadId//threadidentifier)

而关于线程的创建、调度、回收等相关原理,博主在linux开发的专栏详细说明过,因此不再赘述,这里放出相关的链接
线程操作
互斥同步

c++11中与线程相关的库叫做<thread>。而其中线程被封装的类是thread::thread。

thread的相关函数

default (1)	
thread() noexcept;
initialization (2)	
template <class Fn, class... Args>
explicit thread (Fn&& fn, Args&&... args);
copy [deleted] (3)	
thread (const thread&) = delete;
move (4)	
thread (thread&& x) noexcept;

一般情况下使用构造函数2更多,因为其支持可变参数,使用起来更加的灵活,thread::thread不存在拷贝构造函数和赋值重载函数,所以无法将一个thread对象拷贝给另一个thread对象。

这里我们拿构造函数2为例,fn表示未来线程执行的任务,可以是一个函数指针、函数对象、lambda表达式。Args表示执行该任务所需要的参数。

void thread1run(int a, int b) {std::cout <<"thread1 do:"<< a + b << std::endl;
}void thread2run(std::string str) {std::cout <<"thread2 do:" << str << std::endl;
}int main() {std::thread t1(thread1run,30,50);std::thread t2(thread2run,"hello, world");std::thread t3([](std::string str)->void {std::cout << "thread3 do:" << str << std::endl;},"hello,world");t1.join();t2.join();t3.join();return 0;
}
void join();

thread对象一旦创建,就会立即运行,当一个线程执行结束时,需要进行回收,因为线程本质上一种资源,会占用内存当中的空间,如果不进行回收,就会导致内存泄漏。而回收所使用的成员函数为join,比如线程对象t1执行结束,主线程调用t1.join(),就会将t1进行回收,不过前提是t1的任务已经执行完毕,否则主线程将会阻塞等待在t1.join()中,直到t1结束。

既然说一个线程如果执行结束,并不会主动的退出,需要主线程使用join去回收,那么如果我们希望一个线程,他不需要主线程去进行回收,也就是不用去调用join,那么该怎么办呢?答案是使用detach。使用detach后的线程将会是独立的,这意味不需要主线程去join来回收它,当其结束执行后,将会自动退出,不会占用资源。

bool joinable() const noexcept;

如果一个线程对象,需要join来回收,那么这个线程就叫做joinable;如果一个线程对象,不需要join来回收,那么这个线程则是non joinable。成员函数joinable就是用来检测该线程是否是joinable的。若为真,返回true,若为假,则返回false。

那么什么样的线程对象是non joinable的呢?

  • 1、以默认构造函数创建的线程对象。
  • 2、使用detach的独立线程。
  • 3、被join回收后的线程
  • 4、因被move构造而移除的线程
void thread2run(std::string str) {std::cout <<"thread2 do:" << str << std::endl;
}void testjoinable(std::thread& t,std::string t_name) {if (t.joinable()) std::cout << t_name << " is joinable"<<std::endl;else std::cout << t_name << " is non-joinable"<<std::endl;
}int main() {std::thread t1;												//default_constructorstd::thread t2(thread2run,"hello, world");std::thread t3([](std::string str)->void {std::cout << "thread3 do:" << str << std::endl;},"hello,world");t3.detach();testjoinable(t1, "thread1");//thread1 is non-joinabletestjoinable(t2, "thread2");//thread2 is joinabletestjoinable(t3, "thread3");//thread3 is non-joinable//t1.join();t2.join();//t3.join();testjoinable(t2, "thread2");//thread2 is non-joinablereturn 0;
}

要注意,对non_joinable的线程使用join是会导致崩溃的,因此,我们可以在使用join之前,判断一下该线程是否是joinable。

	if(t1.joinable()) t1.join();if(t2.joinable()) t2.join();if(t3.joinable()) t3.join();

thisthread

有没有考虑到一点,线程对象只有在主线程中才能访问到,那么如果我们想要在线程中对该线程进行一些控制,那么该怎么做呢?答案是线程中可以使用namespace thisthread,里面包含了四种函数,注意thisthread中的一切函数,都是只对本线程生效。

thread::id get_id() noexcept;
//获取当前线程的ID
std::thread::id main_thread_id = std::this_thread::get_id(); //主线程 IDvoid is_main_thread() {  //判断是否是主线程的IDstd::cout<<std::this_thread::get_id()<<":" ;
//其他线程使用thisthread::getid,只会获取该线程的IDif (main_thread_id == std::this_thread::get_id())   std::cout << "This is the main thread.\n";elsestd::cout << "This is not the main thread.\n";
}int main()
{is_main_thread();std::thread th(is_main_thread);th.join();
}

线程ID的虽然是一个整数类型,但是在C++中并没有直接用int等整数类型作为线程ID的类型,而是thread::id类型,这是因为不同的平台下对于线程ID有不同的使用标准,所以C++选择统一使用一个类来封装,第二个点就是,线程ID支持在unorder_map等容器中,作为key值索引。

void yield() noexcept;

yield是主动让出当前线程的执⾏权,让其他线程先执行。此函数的确切行为依赖于实现,特别是取
决于使⽤中的OS调度器机制和系统状态。例如,先进先出实时调度器(Linux的SCHED_FIFO)
会挂起当前线程并将它放到准备运⾏的同优先级线程的队列尾。比如下面的案例代码:

std::atomic<bool> ready(false);void count1m(int id) {while (!ready) {             // 等待主进程将ready置为truestd::this_thread::yield();	//如果ready没有为true,那么线程就不会开始计算//因为一直调用yield,切换下一个线程执行,直到ready为true。}for (volatile int i = 0; i < 1000000; ++i) {}std::cout << id;
}int main()
{std::thread threads[10];std::cout << "10个进程比赛谁先算完100万:\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;
}

最后的两个函数与时间有关。分别是sleep_forsleep_until

template <class Rep, class Period>void sleep_for (const chrono::duration<Rep,Period>& rel_time);template <class Clock, class Duration>void sleep_until (const chrono::time_point<Clock,Duration>& abs_time);

其中参数chrono::duration和chrono::time_point都是<chrono>库中的类,<chrono>库也是c++11新增的库,该库与时间相关,也许未来会给大家带来chrono库的介绍。总之,我们可以把time_point视为一个时间点,duration视为一个时间段。这两者有什么区别呢?时间段表示一个范围的时间,而时间点表示一个具体的时间刻度,比如五分钟后,表示一个时间段,而9:30,表示一个时间点。

sleepfor可以让线程休眠一个时间段。

void sleep10s() {for (int i = 10; i > 0; --i) {std::cout << i << std::endl;std::this_thread::sleep_for(std::chrono::seconds(1));}std::cout << "Lift off!\n";
}int main()
{std::thread t1(count1m);t1.join();return 0;
}

而sleep_until可以让一个线程休眠到一个具体的时间点。比如下面的示例:

#include <chrono>         // std::systemclock
#include <iomanip>        // std::puttimevoid SleepNextMin() {using std::chrono::system_clock;std::time_t tt = system_clock::to_time_t(system_clock::now());//获取当前时间struct std::tm* ptm = std::localtime(&tt);std::cout << "Current time: " << std::put_time(ptm, "%X") << '\n';std::cout << "Waiting for the next minute to begin...\n";++ptm->tm_min; ptm->tm_sec = 0;								//将时间点设置为下一分钟0刻std::this_thread::sleep_until(system_clock::from_time_t(mktime(ptm)));std::cout << std::put_time(ptm, "%X") << " reached!\n";
}int main()
{std::thread t1(SleepNextMin);t1.join();return 0;
}

在这里插入图片描述

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com

热搜词