C++多线程编程(真实入门)

目录

一、windows下多线程函数

  1、CreateThread创建线程的过程如下:

   2、 _beginthread()创建线程过程如下: 

二、C++接口跨平台接口

1、使用std::async创建多线程

2、使用std::thread类方法创建多线程

3、std::thread和std::async创建线程区别

4、std::thread类创建多线程demo

5、多线程的应用

 附加知识


一、windows下多线程函数

  1、CreateThread创建线程的过程如下:

C++多线程编程(真实入门!)_Kprogram的博客-CSDN博客_c++多线程编程

   2、 _beginthread()创建线程过程如下: 

   C++多线程——_beginthread()和_beginthreadex_xuanyin235的专栏-CSDN博客

/*************************************************************************************************************/

二、C++接口跨平台接口

/******************C++11中添加了新的线程创建使用方法,常见的介绍如下***************************/

 1、使用std::async创建多线程

std::async创建函数如下:

    template<typename _Fn, typename... _Args>
    future<__async_result_of<_Fn, _Args...>>
    async(launch __policy, _Fn&& __fn, _Args&&... __args);

 async的第一个参数launch为std::launch::async时,创建的任务会创建新的子线程;当第一个函数是std::launch::std::launch时,async不创建新线程,线程为async所在线程。

 (1) 利用std::async异步线程创建过程见:c++11多线程编程(九):std::async介绍与实例_小麒麟的成长之路-CSDN博客_std::async

 (2) 利用std::future获取异步线程函数执行结果:

c++11多线程编程(九):std::async介绍与实例_小麒麟的成长之路-CSDN博客_std::async

 

2、使用std::thread类方法创建多线程

(1)join() 主线程等待创建的子线程运行结束。

    join线程的使用场景是:调用join的函数必须等待join的线程函数执行完成,才可以进行往下执行,否则阻塞调用join函数的线程,直到join线程执行完毕。

 (2)detach() 主线程与创建的子线程分离,主线程不等待子线程运行结束.

       利用detach函数创建线程,detach使用场景:detach()函数会让线程在后台运行,即说明主线程不会等待子线程运行结束才结束。 需要注意的是detach中如果用到了主线程中的变量,需要进行深拷贝,保证使用的变量生命周期没有结束。

3、std::thread和std::async创建线程区别

(1)std::async函数可以跟std::future<T>组合获取线程的执行结果,创建时可能创建新线程,或者不创建新线程(跟async的第一个参数有关)。

(2)std::thread类肯定会创建多线程,如果需要捕获线程中执行结果,可以在线程中将变量赋值给全局变量。

4、std::thread类创建多线程demo

join的用法demo如下:

#include <iostream>
#include <thread>
#include <stdio.h>
#include <vector>
#include <Windows.h>
void func() {
	for (int i = 0; i < 10; ++i) {
		char chIn[50];
		sprintf_s(chIn, 49, "thread::func is : %d", i); //sprintf_s(chIn, 49, "thread::func is : %d\n", i);
		OutputDebugStringA(chIn);//调试控制台打印结果
		std::cout << "thread::func"  << chIn << std::endl; //dos命令窗口打印
	}
}
int main(int argc, char *argv[])
{
	std::vector<std::thread> threads; //存储线程函数的变量
	for (int i = 0; i < 10; i++) {
		threads.push_back(std::thread(func));
	}
	for (auto &thread : threads) {
		thread.join();	//执行线程变量中的每个线程执行函数
	}
	std::thread myFun(func);
	myFun.join();
	return 0;
}

思考:若线程函数使用同一个vector类型的变量进行写操作,会出现资源写入冲突的问题,若使用map代替就不会出现,因为map的键只能唯一;当然,在vector写入的时候加入锁也是可以解决的,但是加锁,在线程处理结果不能立即返回的情况下,会影响性能。程序如下:

#include <iostream>
#include <vector>
#include <thread>
#include <mutex>
#include <map>
#include <Windows.h>
using namespace std;
std::vector<int> vecGa;

std::map<int,int> mapGa;
std::mutex va1;
void fun1(int a)
{
	//std::lock_guard<std::mutex>lock(va1);  // 若不加锁,vector写入时会出现访问异常的问题
	//{
	//	Sleep(100);
	//	std::cout << "iiiiiiiiiiiii " << a << std::endl;
	//	vecGa.push_back(a);
	//}
	
	Sleep(100);
	std::cout << "iiiiiiiiiiiii " << a << std::endl;
	mapGa.insert(pair<int,int>(a,a));
}
int main()
{
	std::vector<std::thread>vecTest;
	for (int i = 0; i < 100; i++)
	{
		vecTest.push_back(std::thread(fun1,i));
	}

	for (auto &thread : vecTest)
	{
		thread.join();
	}
    std::cout << "Hello World!\n";
}

注:多线程共享相同的变量时,存在两种情况:

(1)若线程间都是共同读取一个变量,即访问同一块地址,由于都不会改写该内存的数据,所以不会产生数据修改的竞争,不需要加锁。

(2)若线程间共同修改同一个变量,即修改同一块地址的数据,会出现数据修改的竞争问题,都要加锁。

如:

        nNum = 0;   thread1:    { nNum =+2;}       thread2:{nNum=+5;}

    当thread1运行到修改nNum++时,线程执行的碎片时间到,   thread2获取执行权,nNum值变        为5,这时thread1获取到执行权,这时,thread1里面的变量已经变为了5,执行++操作后,变        为7,这个预期是不相符的。为了保证数据执行的顺序按照预期,可以加锁。

局部变量的创建修改是不用加锁的,因为每个线程里面的局部变量地址是不一样的;公有变量的地址是一样的,所以修改公有变量时必须加锁

若是想安装交替执行可以使用条件变量std::condition_variable ,参考多线程mute/lock_guard/unique_lock/condition_variable访问同一公共资源_hanxiaoyong_的博客-CSDN博客

下面举一个例子

// MutTV.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//

#include <iostream>
#include <vector>
#include <thread>
#include <mutex>
#include <map>
#include <Windows.h>
using namespace std;
std::mutex va1;
int nNum = 0;
void fun1(int a)
{
	{std::lock_guard<std::mutex>lock(va1);
	Sleep(100);
	nNum += 2;
	std::cout << "iiiiiiiiiiiii " << nNum << std::endl;
	}
}
void fun2(int a)
{
	std::lock_guard<std::mutex>lock(va1);
	{
		Sleep(100);
		nNum += 5;
		std::cout << "jjjjjjjjjjjjjjjjj " << nNum << std::endl;
		
	}

}
int main()
{
	std::vector<std::thread>vecTest;
	for (int i = 0; i < 100; i++)
	{
		if (i < 50) {
			vecTest.push_back(std::thread(fun1, i));
		}
		else
		{
			vecTest.push_back(std::thread(fun2, i));
		}
		
	}

	for (auto &thread : vecTest)
	{
		thread.join();
	}
    std::cout << "Hello World!\n";
}

加锁运行情况如下:

 不加锁的运行如下:

thread的线程函数绑定类成员函数如下:

#include <thread>
class A
{
    void func1(int nValue)
    {
        //todo
    }
};

void main()
{
    A a;
    std::thread ths(&Task::func1, &a, 1);
    ths.detach();
    reutrn;
}

 

 5、多线程的应用

(1)多线程函数中若函数参数为引用,则需要使用std::ref(var)。若常引用,使用std::cref(var),具体可参考:

c++ 中ref关键字的应用_c++ ref_qzy0621的博客-CSDN博客

(2)生产者消费者的多线程模式:生产者消费者模式(c++)_c++ 生产者消费者模式_WolfOnTheWay的博客-CSDN博客

 

 附加知识

 创建线程demo可以参考如下链接:

C++11多线程join()和detach()的理解_Stone-CSDN博客_c++ join

C++11多线程thread参数传递问题_A_Bo的博客-CSDN博客_c++ thread 参数

  • 1
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值