• 认识多线程


    线程创建函数介绍


    创建线程可以使用系统提供的API函数:CreateThread来完成,该函数将创建一个线程,其函数声明如下:

    HANDLE WINAPI CreateThread(
      __in_opt   LPSECURITY_ATTRIBUTES lpThreadAttributes,
      __in       SIZE_T dwStackSize,
      __in       LPTHREAD_START_ROUTINE lpStartAddress,
      __in_opt   LPVOID lpParameter,
      __in       DWORD dwCreationFlags,
      __out_opt  LPDWORD lpThreadId
    );
    下面简单介绍CreateThread函数的每个参数:

    lpThreadAttributes

    执向LPSECURITY_ATTRIBUTES结构体的指针,使用时我们一般设置为NULL,表示让该线程使用默认的安全性;

    dwStackSize

    设置线程初始栈的大小,即线程可以将多大的地址空间用于它自己的栈,以字节为单位。默认设置为0,表示使用与调用该函数的线程相同的栈空间大小

    lpStartAddress

    指向应用程序定义的LPTHREAD_START_ROUTINE类型的函数的指针,表明新线程的起始地址,由新线程执行;这个函数的声明必须是如下形式,函数名字不做要求

    DWORD WINAPI ThreadProc(LPVOID lpParameter);

    lpParameter

    新线程的入参,是LPVOID类型,必要时进行相应类型的强制转换;

    dwCreationFlags

    设置用于控件线程创建的附加标记,若为0表示线程创建后就立即执行,若为CREATE_SUSPENDED表示创建后处于暂停状态;

    lpThreadId

    这个是个返回值,指向一个变量,用来接收线程的ID,当线程创建成功后,系统就会分配一个线程ID;如果我们不关心线程ID,我们可以设置为NULL;


    实例1


    新建一个Win32 Console Application类型的工程,并命名为MultiThread.cpp;

    代码如下:

    // MultiThread.cpp : 定义控制台应用程序的入口点。
    //
    
    #include "stdafx.h"
    #include <windows.h>
    #include <iostream>
    using namespace std;
    
    //新线程的起始地址
    DWORD WINAPI  Thread1Proc(LPVOID lpParameter)
    {  
        cout << "Thread1 is ruuning" << endl;
    
        return 0;
    }
    
    int _tmain(int argc, _TCHAR* argv[])
    {
    	HANDLE hThread1 = NULL;
        //创建新的线程
        hThread1 = CreateThread(NULL,0,Thread1Proc,NULL,0,NULL);
        //无须对新线程设置优先级等操作,关闭之
        //良好的编码习惯
        CloseHandle(hThread1);
    
        cout << "main thread is running" << endl;
     
        return 0;
    }
    
    我们执行(Ctrl + F5)这段代码多次后,可以发现其结果有三种情况,导致这情况出现的主要原因是:

    操作系统为每一个运行的线程安排一个的CPUT时间---时间片。系统通过一种循环方式为线程提供时间片,线程仅在自己的时间内运行,因为时间片非常的短,所以给我们的感觉就像多个线程是同时运行的,每次执行时系统分配的时间片都是不一致的,所以有时候能执到线程1,有时候在主线程的时间片内,主线程已经执行完毕,主线程退出,线程1没有机会执行;

    情况1


    情况2


    情况3



    在我们的main函数返回之前,添加一个Sleep(1000)语句,就能确保执行结果和预期一致,即情况3;

    解释如下:

    Sleep(1000)表示让主线程睡眠1秒,Sleep的入参是毫秒为单位;由于主线程进入睡眠状态,主线程放弃了执行的权利,线程1得到了运行权利,线程1在1秒内足以执行完成,线程1结束;在1秒后,主线程睡醒,主线程执行完毕,程序退出;


    实例2


    在这个实例中我们引入一个全局的计数器g_Index,在两个线程中分别打印这个index 和执行的线程标识,其代码如下:

    int g_nIndex = 0;
    const int nMaxCnt = 100;
    
    //新线程的起始地址
    DWORD WINAPI  Thread1Proc(LPVOID lpParameter)
    {  
        while (g_nIndex++ < nMaxCnt)
        {
            cout <<"Index = "<< g_nIndex << " ";
            cout << "Thread1 is ruuning" << endl;
        }
    
        return 0;
    }
    
    int _tmain(int argc, _TCHAR* argv[])
    {
    	HANDLE hThread1 = NULL;
        //创建新的线程
        hThread1 = CreateThread(NULL,0,Thread1Proc,NULL,0,NULL);
        //无须对新线程设置优先级等操作,关闭之
        //良好的编码习惯
        CloseHandle(hThread1);
    
        while (g_nIndex++ < nMaxCnt)
        {
            cout << "Index = " << g_nIndex << " ";
            cout << "main thread is running" << endl;
        }
    
        return 0;
    }
    运行结果:


    我们发现某些行的打印和我们的预期有些不符,例如:“Index = 3Index = 2 main thread is running”,这个就是多线程执行带来的问题,可以使用互斥量来解决,这暂不讨论;

    同时我们也可以发现主线程和线程1是交替执行的,这里也体现的了多线程的执行是按照时间片来运行的

  • 相关阅读:
    小村系列之十八:幸福的桥
    小村系列之十六:改革的石头
    获取<select>,<radio>,<checkbox>中未被选中的value值和被选中的value值
    display:inline-block,block,inline的区别与用法
    Java中List Set Map集合的遍历
    C#自定义List类
    C#获取文件和文件夹大小
    C# winform带进度条的图片下载
    C#委托的详细使用
    asp.net cookie和session的详细使用
  • 原文地址:https://www.cnblogs.com/jinxiang1224/p/8468341.html
Copyright © 2020-2023  润新知