首页 > 编程语言 >C++多线程基本使用方式

C++多线程基本使用方式

时间:2024-07-27 14:25:09浏览次数:10  
标签:多线程 join thread 方式 int C++ 线程 include cout

一、线程创建

        创建线程的函数    thread  t (函数名f,函数 f 的参数) 或者  用lambda表达式

代码:

#include <iostream>
#include <thread>
#include <vector>
using namespace std;

void output(string input,int a) {
	cout << input << endl;
	cout << a << endl;
}

int main() {
	string text = "Hello World ! My name is XXX . Nice to meet you . bla bla bla\n";
	vector<thread> thread_list;
	int a = 10;
	for (int i = 0; i < 2; i++) {
		thread t(output,text,a);
		//thread t([text](int a) {
		//	cout << text << endl;
		//	cout << a << endl;
		//	},a);
//上面为lambda表示法,效果相同,可提高可读性,简化代码,但是运行速度,效率不如内敛函数incline
		thread_list.push_back(move(t));
	}
	for (auto& t : thread_list) {
		if (t.joinable()) {
			t.join();
		}
	}
	return 0;
}

        由于是多线程,导致可能出现不同线程,交替输出甚至短暂截断输出的情况!                                比如:第一个线程输出到 “ Hello World ! My name is XXX .  ” 时,第二个线程就开始输出,然后第一个线程又继续输出剩余内容,最后就是多个线程抢用一个输出,导致输出结果交叉混杂。

        for循环中,给vector添加数据时,使用move函数,相当于将 t 的值直接存入thread_list,不用创建额外thread变量来存入,且move以后,t  就处于未定义状态了

        最后一定要 join 释放资源,否则运行程序会报错!

        

二、线程传参

        左值:等式左边的值

        右值:等式右边的值

        例:a = a+2*b+3;        a是左值,可重复使用;(a+2*b+3)是右值,临时计算的数据

        如果是函数要求左值引用传参,即int& a,则需要用ref()将参数包装起来,否则编译报错

代码:

#include <iostream>
#include <thread>
#include <vector>
using namespace std;

void output(string input,int& a) {//此处  a   为引用传值!
	cout << input << endl;
	cout << ++a << endl;
}

int main() {
	string text = "Hello World ! My name is XXX . Nice to meet you . bla bla bla\n";
	vector<thread> list;
	int a = 10;
	for (int i = 0; i < 2; i++) {
		thread t(output,text,ref(a));//用  ref  包装参数  a
		//thread t([text](int& a) {
		//	cout << text << endl;
		//	cout << ++a << endl;
		//	},ref(a));
		// lambda写法的修改  上同
		list.push_back(move(t));
	}
	cout <<"before join: "<< a << endl;
	for (auto& t : list) {
		if (t.joinable()) {
			t.join();
		}
	}
	cout << "after join: " << a << endl;

	return 0;
}

输出:

        还有一点,左值引用传递时,不可传递右值,否则报错

        如果是右值引用传参,即int&& a ,则不能用ref(),否则会报错

        输出结果中,先打印     before join:  10  ,是因为线程异步,当for循环结束时,由于线程启动、调用传入函数等操作需要时间开销,导致线程还没开始输出,而这时主程序已经运行到cout这一行了,所以就会先输出     before join:  10 

三、Mutex与Atomic

        3.1、mutex(互斥锁)

                用于保护共享数据免受并发访问的冲突,通过阻塞和唤醒线程来管理共享资源的访问。

主要特点:

  1. 互斥访问:确保同一时间只有一个线程可以访问被保护的资源。
  2. 锁的所有权:通过lock()unlock()成员函数来获取和释放锁。通常使用lock_guardunique_lock来自动管理锁的生命周期。
  3. 死锁:不当的使用可能导致死锁,例如,两个线程相互等待对方释放锁。
  4. 上下文切换:当一个线程等待锁时,可能会发生上下文切换,增加系统开销。

优点:

  • 提供了强同步,确保数据的一致性和线程安全。
  • 适用于需要保护复杂数据结构或执行复杂同步操作的场景。

缺点:

  • 可能导致性能开销,特别是在高争用的情况下。
  • 需要正确管理锁的获取和释放,否则可能引发死锁。

代码:

#include <iostream>
#include <mutex>
#include <thread>

mutex mtx; // 创建互斥锁

void printID(int id) {
    lock_guard<mutex> lock(mtx); // 锁定互斥锁
    cout << "ID: " << id << endl;
}

int main() {
    thread t1(printID, 1);
    thread t2(printID, 2);
    
    t1.join();
    t2.join();
    
    return 0;
}

        3.2、atomic(原子锁)

                C++11引入的用于实现原子操作的模板类。原子操作保证在多线程环境中,单个操作不可分割,不会出现中间状态。

主要特点:

  1. 不可分割的操作:原子操作要么完全执行,要么完全不执行,不会出现中间状态。
  2. 内存顺序:提供不同内存顺序选项,如memory_order_relaxedmemory_order_acquire等。
  3. 不阻塞:原子操作通常不会阻塞线程,而是通过硬件支持来实现同步。
  4. 只支持基本类型:主要支持基本数据类型(如整数类型、指针类型)。

优点:

  • 性能较高,特别是对于简单的数据类型和操作。
  • 不会引起线程阻塞,减少了上下文切换的开销。

缺点:

  • 功能有限,主要适用于简单的数据类型和操作。
  • 需要仔细处理内存顺序,以避免数据竞争和不一致性。

代码:

#include <atomic>
#include <thread>

atomic<int> count(0); // 创建原子变量

void increment() {
    count.fetch_add(1, memory_order_relaxed); // 令 count 的值 +1
}

int main() {
    thread t1(increment);
    thread t2(increment);

    t1.join();
    t2.join();

    cout << "Final count: " << count << endl;

    return 0;
}

四、待完善。。。

        (1)join

                join函数会阻塞线程,直到线程函数执行结束,再执行下面语句

        (2)使用detach

                detach会使线程和线程对象分离,让线程作为后台线程去执行

标签:多线程,join,thread,方式,int,C++,线程,include,cout
From: https://blog.csdn.net/ANEW1001/article/details/140696122

相关文章

  • Dynamsoft Barcode Reader SDK C++ 10.4.10 Crack-24.7.22
    DynamsoftBarcodeReaderDocumentationforC++EditionDynamsoftBarcodeReader(DBR)SDKC++EditionisabarcodereadingtooldesignedspecificallyforC++developers.LeveragingtheefficiencyandflexibilityoftheC++language,DBRC++Editionprovid......
  • 【C++初阶】vector
    【C++初阶】vector......
  • 六、IPv6基础知识-地址配置方式
    1.IPv6地址配置方式静态:手工配置静态地址动态:无状态地址自动配置:基于NDP实现,不需要IPv6地址分配服务器保存和管理每个节点的状态信息有状态地址自动配置:基于DHCPv6实现,IPv6地址分配服务器必须保存每个节点的状态信息,并管理这些保存的信息2.无状态地址自动配置无状态自......
  • 分布式集群与多线程高并发
     后台数据的处理语言有很多,Java是对前端采集的数据的一种比较常见的开发语言。互联网移动客户端的用户量特别大,大量的数据处理需求应运而生。可移动嵌入式设备的表现形式很多,如PC端,手机移动端,智能手表,Google眼镜等。Server2client的互联网开发模式比较常见,有一种新的数......
  • [C++] 小游戏 斗破苍穹2024暑假 版本 zty出品
           大家好今天zty带来的是斗破苍穹的2024年暑假版本,主要剧情为成为徐梓煜徐梓煜_SHARK-CSDN博客,一脚踹飞zty,玩法比较偏娱乐。感谢: 徐梓煜_SHARK-CSDN博客 徐梓煜和他的父亲Cpp_King-CSDN博客姜乙和李明泽以及杨盛策(没有CSDN号)先赞后看养成习惯code#i......
  • c++计算器
    因为用的是MFC,所以就不把代码贴出来了,安装包在文章顶部,在此只把另一个计算器的代码贴出来:#include<cstdio>#include<cmath>#include<iostream>#include<string>#include<windows.h>usingnamespacestd;intAtoi(strings,intradix){ intans=0; for(inti=0;i<s.s......
  • c++ 线程函数传递数据 对象和变量
         CMakeLists.txtcmake_minimum_required(VERSION3.10)project(MyProject)#查找并添加线程库find_package(ThreadsREQUIRED)#添加可执行文件add_executable(my_programmain.cpp)#添加线程库链接target_link_libraries(my_programThreads::Threa......
  • [C++]广度优先遍历
       代码与图见上图思路定义一个一维数组(char)和一个二维数组(int),一个bool类型数组来判断该节点是否被访问过。函数中定义队列,对各个结点进行入队出队,并标记为已访问。当该邻结点未被标记且与该节点连接,进行上述操作。注意:for循环的i变量初始赋值随二维数组而变化。如:......
  • C++文件系统操作6 - 跨平台实现文件和文件夹的拷贝
    1.关键词2.fileutil.h3.fileutil.cpp4.filesystem_win.h5.filesystem_win.cpp6.filesystem_unix.cpp7.源码地址1.关键词C++文件系统操作拷贝文件拷贝文件夹跨平台2.fileutil.h#pragmaonce#include<string>#include<cstdio>#include<cstdint>#i......
  • 【C++/STL】map和set介绍
    ......