c++ 多线程编程(1)—创建线程的常用方法

175 阅读1分钟

这是我参与2022首次更文挑战的第14天,活动详情查看:2022首次更文挑战

线程是一个让人又爱又恨的,对于普通程序员,前端也好后端也好,很少有机会会接触到线程这个东西。线程出现主要是性能或者并发。这些都是框架设计人员应该考虑的。不多在面试中可能会被问到,

线程和进程

什么是线程,这个 CPU 调度最小单元。一个进程可以有多个线程,线程开销要小于进程,不过线程容器发生共享资源。在 window 系统来说,进程开销比较大,所以在 window 中是鼓励大家在进程下进行多线程编程。而在 linux 系统下,创建一个进程和创建一个线程的开销差不多,

分享来回的一个问题,就是如何理解线程,今天侧重实践,关于线程原理和概念就点到为止。

int main()
{
	return 0;
}

每一个我们写应用都是运行在一个线程上,默认线程就是 main() ,线程可以看成轻量级的进程,比较理想方式就是将进程分为多个平行执行的线程。

#include <iostream>
#include <thread>
#include <chrono>
#include <algorithm>

using namespace std;
using namespace std::chrono;

typedef unsigned long long ull;

ull OddSum = 0;
ull EvenSum = 0;

我们可以用 typedefunsigned long long 类型定义一个别名ull

然后来创建两个函数分别是 findEvenfindOdd ,分别是在数列中对所有偶数进行求和对所有奇数进行求和

void findEven(ull start, ull end)
{
	for (ull i = start; i <= end; i++)
	{
		if ((i & 1) == 0) {
			EvenSum += i;
		}
	}
}

void findOdd(ull start, ull end)
{
	for (ull i = start; i <= end; i++)
	{
		if ((i & 1) == 1) {
			OddSum += i;
		}
	}
}

我们创建一个比较大序列 ull start = 0, end = 1900000000 然后分别调用findOddfindEven 来分别对其中奇数和偶数进行求和


int main()
{
	ull start = 0, end = 1900000000;
	
	findOdd(start, end);
	findEven(start, end);
	cout << "OddSum : " << OddSum << endl;
	cout << "EvenSum : " << EvenSum << endl;
	return 0;
}

接下来我们引入 high_resolution_clock 来计算执行函数所消耗的时间,以便于我们对比引入线程前后之间差别

    auto startTime = high_resolution_clock::now();

    findOdd(start, end);
    findEven(start, end);

    auto stopTime = high_resolution_clock::now();
    auto duration = duration_cast<microseconds>(stopTime - startTime);

    cout << duration.count() / 1000000 << endl;
    8
OddSum : 902500000000000000
EvenSum : 902500000950000000

早在 c++ 11 版本之前,在语言层面是不支持线程的,需要调用系统 API 来实现线程,这样一来就给开发者带来许多不便。

我们对上面程序进行优化,所谓的优化就是将线程引入,我们第一个 work 类型为 thread,然后我们将函数作为第一个参数传入到 work_find_odd 如果传入线程的函数没有参数,只要这样做就可以了,如果函数有参数,我们就将参数从第二个参数起依次传入到线程,因为第一个参数是函数,为了让主线程main 等待这两个线程执行完后在继续执行,我们可以调用线程的 join 方法。接下来会演示几种常见创建线程的方法。

int main()
{
	ull start = 0, end = 1900000000;

	auto startTime = high_resolution_clock::now();

	std::thread work_find_odd(findOdd, start, end);
	std::thread work_find_even(findEven, start, end);
	
	//findOdd(start, end);
	//findEven(start, end);

	work_find_even.join();
	work_find_odd.join();

	auto stopTime = high_resolution_clock::now();
	auto duration = duration_cast<microseconds>(stopTime - startTime);

	cout << duration.count() / 1000000 << endl;

	cout << "OddSum : " << OddSum << endl;
	cout << "EvenSum : " << EvenSum << endl;
	return 0;
}

创建线程方式

指针函数

void fun(int x) {
	while (x-- > 0)
	{
		cout << x << endl;
	}
}


int main()
{
	std::thread work_one(fun, 11);
	work_one.join();
	return 0;
}

Lambda 函数

int main()
{
	auto fun = [](int x) {
		while (x-- > 0)
		{
			cout << x << endl;
		}
	};

	std::thread work_one(fun, 11);
	work_one.join();
	return 0;
}

class Base {
public:
	void operator ()(int x) {
		while (x-- > 0) {
			cout << x << endl;
		}
	}
};
std::thread work_one(Base(), 11);

类的方法

将类的方法传入到线程

class Base
{
public:
	void run(int x) {
		while (x-- > 0)
		{
			cout << x << endl;
		}
	}
};
std::thread work_one(&Base::run,&b, 11);

类的静态方法

这一次因为是类的静态方法所以不需要传实例&Base::run 然后传入方法run 的参数

class Base
{
public:
	static void run(int x) {
		while (x-- > 0)
		{
			cout << x << endl;
		}
	}
};

std::thread work_one(&Base::run, 11);