Windows多线程编程 – 基础

DinS          Written on 2017/8/26

本专题开始介绍多线程编程。但是类别不是在std下吗?为什么要介绍windows多线程编程?因为标准库提供的多线程工具是对多线程问题的一个抽象,并非完全与多线程问题的概念一一对应。相比之下,windows提供的工具更加贴切,因为本专题将从windows入手,着重介绍多线程编程的问题和概念,当读者熟悉了这些概念后再进入标准库的多线程编程

再次强调一遍,介绍windows多线程编程是为了让读者熟悉概念,对于具体的代码可以不必太过专注。

一、基础概念辨析

进程(process)与线程(thread)的关系:这是一道经典的面试题,在这里并不就这个问题展开讨论,只想用一个例子形象的说明。
你打开了一个mp3播放器,开始听音乐,这个播放器是一个进程。与此同时你打开了文字处理程序word,这又是一个进程,其中一个线程用于接收用户输入,一个线程用于检查语法,一个线程用于调度打印机,这三个线程同时在工作,各司其职。

为何要使用线程?
试想刚才的那个word,如果你打开的是有100多页的一个doc,并且检查语法的不是一个线程,那么程序会花1-2分钟来检查所有页的语法,此时程序不会响应任何操作,等于程序处于“假死”状态,用户当然不会高兴了。
对于十分耗时的操作,比如IO和等待socket之类的,开辟线程来处理是一种思路。

线程与核心的关系
线程与核心并没有什么天然的关系。
在过去只有一个CPU的时代,照样可以多线程,不过有了多核心后,从线程的理解出发,理应是一个线程对应一个核心,这样能充分发挥多核心的优势。
线程与核心的对应关系由操作系统完成,程序员不需要过多关注。

并发(concurrent)与并行(parallel)
这两个概念与线程和核心数有微妙的联系
并发指的是在一个CPU上多个进程、线程迅速切换,看起来好像这些进程、线程在同时工作。注意这些东西并没有真正的同时工作,只不过切换的非常迅速用户感觉不出来而已。
并行指的是一个程序在多个CPU上同时运行,达到提高效率的效果。这里是真正的同时运行。
多线程属于哪个呢?
在单核时代,毫无疑问是并发。在多核时代,要看操作系统如何安排了,如果操作系统把多个线程分到了多个核心,那么就是并行。如果所有线程仍然在一个核心上,就是并发。

多线程与OpenMP
多线程并不一定意味着程序效率的提高,更多情况下还是为了处理耗时操作。
如果想充分利用多核心优势,进行并行编程,那么可以使用OpenMP或者其他库,跨平台并且经过了抽象,使用起来更方便,而且是国际通行标准。
关于OpenMP讨论见另一个专题

二、多线程基础

使用线程其实很简单,看如下代码。

点击这里看大图

输出结果:

虽然跟我们设想的不太一样,但毕竟确实有5条输出,来自不同的线程。
看起来杂乱是因为这里涉及到线程间通信问题,之后再讨论

使用过程的注意事项都在注释里说明了。
所谓线程实际上在编程中体现为一个函数,使用函数地址来开辟新线程。
这个例子的作用就是开辟5个线程,等待线程全部执行完毕再继续
WaitForMultipleObjects是windows函数,可自行百度。

需要额外说明的恐怕是stdcall。
你可能还会见到cdecl,fastcall等等。这些都称为函数的调用约定,具体而言跟汇编有关系,在汇编中函数的参数通过寄存器压栈传递。感兴趣可见《函数调用过程——汇编的视角》。
如何传递?以及谁负责清理这些参数?这就是调用约定的作用。
_stdcall规定参数采用从右到左的压栈方式,被调函数自身在返回前清空堆栈,WIN32 Api都采用_stdcall调用方式。
_cdecl是C/C++的缺省调用方式,参数采用从右到左的压栈方式,传送参数的内存栈由调用者维护。
_fastcall调用较快,它通过CPU内部寄存器传递参数。

在项目属性中可以看到这个

也就是说,当我们写一个函数,默认是__cdecl调用方式,但是_beginthreadex里接收的参数必须是__stdcall,于是我们显示指定函数的调用方式是__stdcall。

另一个要说明的问题就是传入的参数是void*的问题。
线程函数只能接受一个参数,即void*,这是规定死了的,那么如何向其传递多个参数呢?
答:利用void*
因为void*可以指向任何类型,所以我们可以定义一个结构来达到传递多个参数的目的,见下:

点击这里看大图

运行结果

这种方式在windows多线程编程中非常常见,虽然void*平常用的不多,但是在这里一定要认识。

到这里应该没有什么难度,只不过是介绍开辟线程的写法,接下来是难点:如何处理线程间通信问题,见《Windows多线程编程 – 互斥问题》和《Windows多线程编程 – 同步问题》。

参考资料:秒杀多线程http://blog.csdn.net/morewindows/article/details/7392749