从代码分析多线程工作原理

##本半仙呕心沥血所作,能阅读此卷实乃汝之所幸。

什么是进程:进程是指在系统中正在运行的一个应用程序,每个进程之间是独立,且运行在其专用且受保护的内存空间内。如开打的一个exe就是一个进程。

什么是线程:进程要想执行任务,必须得有线程。每个进程至少有一个线程,线程是进程的基本执行单元,一个进程的所有任务都是在线程中执行。同一个进程的各个线程可以共享该进程所拥有的资源,这一点很关键,我们可以利用这一点来做到线程之间的通信。

一个进程好比一个工厂,工厂要生产东西就必须要有生产流水线。如果一个工厂只有一个躯壳而没有生产流水线是无法生产出产品的。这也就是为什么一个进程至少有一个线程的原因。但是往往一条生产流水线的工作效率是很有限,需要为工厂多增加几条生产流水线。即一个进程启动多个线程了完成任务。属于工厂的资源,任何一条流水线都可以使用,也就是一个进程的资源各个线程都可以共享。就好比公司的饮水机,是属于公司所有不属于某一个线程所有,一个员工表示一个线程的话,那么每个员工都可以去饮水机打水。

include<stdlib.h>
include<Windows.h>
void main()
{
     MessageBoxA(0,''ABC",''QWE",0);//弹出一个对话框
     MessageBoxA(0,''ABC",''QWE",0);
     MessageBoxA(0,''ABC",''QWE",0);
     MessageBoxA(0,''ABC",''QWE",0);
     MessageBoxA(0,''ABC",''QWE",0);
}

上面一段代码,目的是弹出5个对话框,没有做任何特殊的处理。我们运行这段代码就是运行一个进程,且是单线程的。先弹出一个对话框,点击确定再弹出第二个…以此类推一共弹出5个。

在这里插入图片描述
现在我们用多线程并发来实现,同时弹出5个对话框的功能。也就是说我们需要5个线程来完成这个任务,一次性给它搞定。

include<stdlib.h>
include<process> //多线程头文件
include<Windows.h>

void run(void *p)//参数为一个空指针,可以指向任何类型
{
     MessageBoxA(0,''ABC",''QWE",0);//弹出一个对话框
}
void main()
{
     for(int i = 0;i<5;i++)
     {
         _beginthread(run,0,NULL);//使用一个for循环启动5个线程
         //第一个参数为线程要执行任务的函数指针
         //第二个参数为线程栈的大小,0表示使用默认值,即一个栈的默认大小为1M
         //这个大小值是由编译器维护了,如果需要更大的栈空间,我们可以手动修改。
         //第三个参数表示任务函数的传入参数。
     }
     
}

运行结果如图所示:在这里插入图片描述
我们成功的使用多线程并发一次性完成5个窗口的弹出。通过系统工具查看一下该进程与线程的状态:在这里插入图片描述
蓝色为我们该程序的进程,我们看到该进程里面有6个线程,莫慌,从上往下第三个线程为进程的主线程,它负责创建其他的子线程。就是这样子。

我们来进阶一下多线程

include<stdio.h>
include<stdlib.h>
include<process>

void gogo(void *p)
{
    int i = 0;
    while(1)//启动一个while循环,每次循环间隔1秒,i自加一次,i>10时结束当前线程
    {
        if(i>10)
        {
            printf("%d",i);
            _endthread();//结束当前线程,与beginthread()正好相反
        }
        i++
        Sleep(1000);
    }
}
void time(void *p)
{
    int i = 0;
    while(1)
    {
        char str[100] = {0};//定义一个字符数组,长度100,初始化为0
        sprintf(str,"title 当前时间第%d",i);//将字符向str输出,即将字符写入到str
        i++;
        system(str);//系统执行,改变黑框的标题,显示出时间
        Sleep(1000);//每次循环间隔一秒钟
    }
}
void main()
{
     _beginthread(time,0,NULL);//同样启动多线程来执行time函数
     for(int i = 0;i<5;i++)
     {
          _beginthread(gogo,0,NULL);
          Sleep(1000);//延迟1秒创建一个线程
     }
}

花两分钟就阅读完上面代码,我们要的事情就是两件,启动一个子线程执行time函数。for循环启动5个子线程,执行gogo函数。time函数负责在黑框标题显示时间,gogo函数在运行到第11秒的时候结束自己当前的线程并且打印i的值,那么就是有5个gogo函数被执行,打印,结束。main函数里面延迟1秒创建一个线程,所以结束线程的时候就是一秒钟结束一个。
在这里插入图片描述
如图所示,我们可以看出多线程直接是并发的,且他们的执行相互不影响,各自己做自己的事情即可。如果我们想让自己创建的多线程不并发,即串行执行。也就是说多个线程,一个执行完再到下一个线程成开始执行,以此类推。我们只需要修改main函数里面for循环的代码如下

for(int i = 0;i<5;i++)
     {
          HANDLE hd = _beginthread(gogo,0,NULL);//使用hd接收开启线程的返回值为线程的编号其实是一个整数类型,HANDLE也是一种数据类型。
          WaitForSingleObject(hd,INFINITE);//阻塞线程,等待上一个线程执行完毕再开始下一个线程
          Sleep(1000);//延迟1秒创建一个线程
     }

所以我们知道多线程的调度有两中模式,一种并发,一种串行。
我们可以使用多线程并发来解决很多问题,如在一个大数据下查找一个数。将数据集分成10份,给10个线程并发查找。这样岂不是快很多。当其中一线程找到之后还可以通知其他还在找的线程不用再找了,我已经找到了。

下回分解。