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

前言

本篇博客使用的库:<windows.h> 提供的线程api(当然是使用操作系统自己提供的线程库的最好了啊)(关于线程函数的>官方文档<)

看博客前需要一些基本条件条件:

  1. 对线程有基本的理解
  2. 简单的C++面向过程编程能力

博客主要内容

  1. 创造单个简单的线程。
  2. 创造单个带参数的线程。
  3. 如何等待线程结束。
  4. 创造多个线程,并使用互斥量来防止资源抢占。

会使用之后,直接跳到“汇总”,复制模板来用就行
相关博客:实现一个简单的线程池

线程教程

创建一个线程:CreateThread()

  1. 函数声明
//返回值:一个HANDLE类型的值,表示线程的句柄,可用于等待线程等函数
HANDLE CreateThread(
  LPSECURITY_ATTRIBUTES   lpThreadAttributes, // 不重要,一般设置为NULL
  SIZE_T                  dwStackSize,        // 堆栈大小,不重要,一般设置为0,表示使用默认堆栈大小
  LPTHREAD_START_ROUTINE  lpStartAddress,     // 函数指针,传入函数名或指针即可
  __drv_aliasesMem LPVOID lpParameter,        // 参数指针,供函数使用
  DWORD                   dwCreationFlags,    // 不重要,一般设置为0
  LPDWORD                 lpThreadId          // 指针,用于保存线程的id,一般设置为NULL
);]
  1. 使用方法
// 声明线程函数的模板:
DWORD WINAPI threadname(LPVOID lpParamter) // 函数名字可随意
{
	/*
	这里填入你的代码
	*/
	return 0L;
}

// 根据声明的函数创造一个线程
// 若函数没有参数,传入函数名字即可,其它参数参考下方示例
 HANDLE hThread = CreateThread(NULL, 0, threadname, NULL, 0, NULL);
  1. 使用实例
    记得等待线程结束!不然函数(不一定是主函数哦,而是CreatThread所处的函数)结束退出后,会释放资源,导致未结束的线程产生各种奇怪的错误!
#include<iostream>
#include<windows.h>

using namespace std;

// 编写了一个我的线程函数
DWORD WINAPI MyThread(LPVOID lpParamter)
{
	cout << "fuck the multithread !\n";
	return 0L;
}

int main ()
{
	// 创造线程
	CreateThread(NULL, 0, MyThread, NULL, 0, NULL);
	// 记得等待线程结束
	system("PAUSE");
	return 0;
}

运行结果:

fuck the multithread !

创建一个带参线程:lpParameter参数

  1. 关于void指针

lpParamter 跟 void指针使用方法类似,void指针可以指向任何数据,所以我们可以把任何类形指针的值赋给void指针(但不能把void指针的值直接赋给其它类型的指针!)
下方是int 与 void 指针相互转换的例子。

int a = 0;
int *intp = &a;
// int指针 转 void指针
void * voidp = intp; // 合法!不会报错

// void指针 转 int指针
intp = voidp; // 不合法! void* 指针不能直接赋值给 int*指针,会报错!
intp = (int *)voidp// 合法!利用强制类型转换,告诉编译器,voidp指针指向的是int数据。
  1. 直接实例

我假设需要传入一个整形参数,在声明函数时,只需要多一行需要这样的操作:

DWORD WINAPI MyThread(LPVOID lpParamter)
{
	// 把lpParamter当成void指针就完事儿了
	int *a = (int *)lpParamter;
	cout << "I have " << a[0] << " dolors!\n";
	return 0L;
}

而创造函数时候,传入一个整形指针就行了

int main ()
{
	int a = 0;
	int *p = &a;
	// 创造线程,注意我把p指针作为参数传入了,它也就成为了上方函数的lpParamter
	CreateThread(NULL, 0, MyThread, p, 0, NULL);
	// 记得等待线程结束
	system("PAUSE");
	return 0;
}

运行结果:

I have 0 dolors!

至于传入更多的参数,可以自己设计一个struct或者class数据结构,并传入其指针就行了,或者也可以传入数组。
至于返回值,也可以利用传入的指针来接收。

等待指定线程结束:WaitForSingleObject()

这个特别简单,此函数就两个参数,第一个参数是创建线程时可得到的返回值(HANDLE)也就是句柄,第二个参数不用关心,传入INFINITE就行了。

使用实例:

HANDLE hThread = CreateThread(NULL, 0, thread1, NULL, 0, NULL);
// 利用得到的句柄等待线程结束
WaitForSingleObject(hThread, INFINITE);

有了这个函数,再也不用Sleep或者While(1)啦。

多线程资源加锁:CreateMutex()

1.创建多个线程

动动脑子都能想到,直接利用循环或多次调用GreatThread函数不就行了么对吧

代码实例

DWORD WINAPI MyThread(LPVOID lpParamter)
{
	// 把lpParamter当成void指针就完事儿了
	int *a = (int *)lpParamter;
	cout << "I have " << a[0] << " dolors!" << endl;
	return 0L;
}

int main()
{
	int a[10];

	for (int i = 0; i < 10; i++)
	{
		a[i] = i;
		HANDLE hThread = CreateThread(NULL, 0, MyThread, a + i, 0, NULL);
	}
	system("PAUSE");
	return 0;
}

但是,直接运行程序,你会发现输出结果特别诡异!顺序完全乱掉了,换行也乱掉了。

I have I have I have 8I have 7I have 5I have 1I have 3 dolors!4 dolors!
2 dolors! dolors!
 dolors!
 dolors!
 dolors!
I have 6I have  dolors!


9 dolors!
I have 0 dolors!

动动脑子也能知道,线程几乎是同时运行的,谁想输出就输出了,管你是不是刚输出到了一半。

所以我们就加锁,使 “输出” 这个资源,只能在一个时间内被单个线程使用

  1. 为资源加锁

创建方法:

// 声明一个句柄
HANDLE cout_mutex;
// 创建一个锁
cout_mutex = CreateMutex(NULL, FALSE, NULL);

使用方法:

// 等待其它线程释放锁,并获取锁的使用权
WaitForSingleObject(cout_mutex, INFINITE);

// 获取锁之后,只要没有解锁,其它线程就会阻塞在WaitForSingleObject()语句。
做一些工作(使用需要互斥的资源等)

// 解锁!
ReleaseMutex(cout_mutex);

使用实例:

HANDLE cout_mutex;

DWORD WINAPI MyThread(LPVOID lpParamter)
{
	// 把lpParamter当成void指针就完事儿了
	int *a = (int *)lpParamter;
	WaitForSingleObject(cout_mutex, INFINITE);
	cout << "I have " << a[0] << " dolors!" << endl;
	ReleaseMutex(cout_mutex);
	return 0L;
}

int main()
{
	int a[10];
	cout_mutex = CreateMutex(NULL, FALSE, NULL);
	for (int i = 0; i < 10; i++)
	{
		a[i] = i;
		HANDLE hThread = CreateThread(NULL, 0, MyThread, a + i, 0, NULL);
	}
	system("PAUSE");
	return 0;
}

运行结果:

可以看出输出正常了。

I have 0 dolors!
I have 1 dolors!
I have 7 dolors!
I have 5 dolors!
I have 4 dolors!
I have 6 dolors!
I have 3 dolors!
I have 9 dolors!
I have 2 dolors!

指定线程运行核心:SetThreadAffinityMask()

现在的cpu一般都是多核的了(任务管理器->性能 -> CPU 即可看到信息),所以有时候需指定线程到某个核心,可以更主动的对cpu资源进行分配。

  1. 函数声明
DWORD_PTR SetThreadAffinityMask(HANDLE hThread, DWORD_PTR dwThreadAffinityMask);
  1. 使用方法:

第一个参数是线程的句柄(HANDLE),第二个参数用来指定CPU核心

比如,你要指定进程到第0个CPU上,则mask=0×01
第1个CPU:mask=0×02
第2个CPU:mask=0×04 (注意不是0×03)
第3个CPU:mask=0×08
以此类推。
如果要指定多个CPU:
比如第0、1个:mask=0×03
第1、2个:mask=0×06
以此类推。
如果CPU个数不足,则会进行取模操作。比如一共4个CPU,则mask=0×0010则和0×01一样
(此段文字转自其它博客,但找不到源链接,就不贴出来了)

  1. 代码实例:
// 绑定到第三个核心
HANDLE hThread = CreateThread(NULL, 0, MyThread, a + i, 0, NULL);
SetThreadAffinityMask(hThread, 0x08);

汇总

线程函数

DWORD WINAPI threadname(LPVOID lpParamter) // 函数名字可随意
{
	int *a = (int *)lpParamter;
	return 0L;
}

创建线程

HANDLE hThread = CreateThread(NULL, 0, threadname, NULL, 0, NULL);

等待线程

WaitForSingleObject(hThread , INFINITE);

互斥量

HANDLE cout_mutex;
cout_mutex = CreateMutex(NULL, FALSE, NULL);

WaitForSingleObject(cout_mutex, INFINITE);
ReleaseMutex(cout_mutex);
发布了18 篇原创文章 · 获赞 36 · 访问量 4万+
展开阅读全文

没有更多推荐了,返回首页

©️2019 CSDN 皮肤主题: 像素格子 设计师: CSDN官方博客

分享到微信朋友圈

×

扫一扫,手机浏览