• C++多线程1.createthread


    C++ 多线程知识1.多线程入门 CreateThread 20131021

    1.介绍WinAPI中的CreateThread

             函数原型:

             HANDLE WINAPI CreateThread(

                       LPSECURITY_ATTRIBUTES       lpThreadAttributes,

                       SIZE_T dwStackSize,

                       LPTHREAD_STACK_ROUTLINE lpStartAddress,

                       LPVOID lpParameter,

                       DWORD dwCreationFlags,

                       LPDWORD lpThreadId);

    参数介绍:

    第一个参数是指向SECURITY_ATTRIBUTES struct 类型的指针,一般设置成为NULL,在Windows 98系统中会忽略该参数。

    第二个参数是用于新线程的初始化堆栈大小,默认值是0,也就是在任何情况下,Windows会根据需要动态的延长堆栈的大小。

    第三个参数是指向线程运行函数的指标,函数名称没有限制,但是必须是以下形式声明的:

             DWORD WINAPI ThreadProc(PVOID pPrama); 返回值是一个DWORD 4字节的数据,参数是一个PVOID的指针,也就是void类型的指针,函数中根据需要,利用指针获取参数。

    第四个参数就是传递给线程函数参数的指针,类型是LPVOID;

    第五个参数通常设置成为0,表示创建完成线程之后,立即执行该线程,当简历线程不马上执行的话,可以使用CREATE_SUSPENED,线程将暂停,知道呼叫ResumeThread()函数类恢复线程。

    第六个参数是一个指标,指向接受执行ID值的变量;

    2.WINAPI CreateThread的操作系统层面分析

             当使用CreateProcess WINAPI调用的时候,系统将创建一个进程和一个主线程。CreateThread将在主线程的基础之上创建一个新的线程,操作系统的底层实现原理如下:

             在内核对象上分配一个线程标识/句柄(也就是一个指针),有CreateThread函数返回;

             把线程退出码设置成为STILL_ACTIVE, 把线程挂起的数目++(也就是这个线程处于SUSPENED状态);

             给线程分配context结构,也就是线程上下文;

             分配两页的物理存储已准备栈,保护页设置成为PAGE_READWRITE,第二页设置成为PAGE_GUARD;

             lpStartAddress和lpvThread值放在栈顶,使他们成为传送给StartOfThread的参数;

             把Context线程上下文的栈指针指向栈顶,指令指向startOfThread函数。

    3.CreateThread的例子

    3.1简单的创建线程启动

    DWORD /*WINAPI*/ ThreadProc(LPVOID lpdwThreadParam){

        for (int i = 0; i < 10; i++){

            cout << "Thread fun display " << i <<  endl;

            Sleep(1000);

        }

        return 0;

    }

     

    int main(){

        HANDLE hThread = CreateThread(NULL, 0,(LPTHREAD_START_ROUTINE) &ThreadProc, NULL, 0, NULL);

        CloseHandle(hThread);

        for (int i = 0; i < 10; i++){

            cout << "main thread run " <<i <<  endl;

            Sleep(1000);

        }

        cout << endl;

        return 0;

    }

    需要注意的地方:

        在传递函数指针的时候,如果我们的ThreadProc没有声明是WINAPI格式,那么在传递函数指针的时候,需要将它转换成为(LPTHREAD_START_ROUTINE)&ThreadProc,但是如果线程处理函数声明形式为WINAPI的话,就可以直接传递函数名称。

    3.2线程之间同步的问题

        这种情况下,会出现输出不换行,或者是换两行的问题。但是初步的 多线程已经具备。多线程并发的运行使用一些资源的话,我们不能够保证这些资源都能够被正确的利用,因为整个时候资源并不是独占的。输出到屏幕就会占用资源,但main线程没有输出endl换行的时候,就让给另一个线程,就会出现各种悲剧的场景;

        解决上面的问题,就需要使用同步机制,对于一个资源被多个线程共用,会造成程序的错误,一个线程拥有对贡献资源的独占,就可以解决上面的问题了:

    HANDLE CreateMutex(

        LPSECURITY_ATTRIBUTES lpMutexAttributes,//SD

        BOOL bInitialOwner,// initial owner

        LPCTSTR lpName // object name

    )

    干函数用于创造一个独占资源,第一个参数我们没有使用设置成为NULL,第二个参数是他的进程,第三个参数是指定资源的名称

        HANDLE hMutex = CreateMutex(NULL,TRUE, “screen”);

        这一条语句创建可一个名称是screen的并且归属创建它的进程的资源

        对应占有资源就有释放资源,一旦进程释放资源,该资源就不属于他了,

        BOOL ReleaseMutex(HANDLE hMutex);

       

        释放资源之后如果再想申请资源也是可以的

        DWORD  WaitForSingleObject(

            HANDLE hHandle,// handle to object

            DWORD dwMilliseconds // time-out interval);

        第一个参数是申请资源的句柄,第二个参数一般指为INFINITE,表示一直等待资源,如果指定0的话,就是一旦得不到资源就立刻返回,也可以指定在一定的时间内返回。

    源代码例子:

    #include <iostream>

    #include <Windows.h>

    using namespace std;

     

    HANDLE hMutex;

     

    DWORD WINAPI Fun(LPVOID lpParamter){

        for (int i = 0; i < 10; i++){

            WaitForSingleObject(hMutex, INFINITE);

            cout<< "Fun display" << i << endl;

            ReleaseMutex(hMutex);

            Sleep(1000);

        }

        return 0;

    }

     

    int main(){

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

        hMutex = CreateMutex(NULL, FALSE, "screen");

        for (int i = 0; i < 10; i++){

            WaitForSingleObject(hMutex, INFINITE);

           

            cout << "main thread run " <<i <<  endl;

            Sleep(1000);

            ReleaseMutex(hMutex);

        }

        cout << endl;

        CloseHandle(hThread);

        CloseHandle(hMutex);

        return 0;

    }

    3.3关于线程等待的问题

    当主线程结束的时候,分支线程还没有结束的话,进程会直接退出,分支线程也会结束,所以在程序中有可能会出现分支线程还没有运行完成,但是主线程已经结束,那么分线程也不会继续运行下去,直接被卡掉。所以这个时候我们需要做一些处理。

        同时注意一个函数 CloseHandle(HANDLE)函数式用于关闭HANDLE的。

        主线程中每创建一个线程的话,会返回一个线程的句柄HANDLE,事后,我们就可以对线程执行某些操作,比如查询线程的状态等考的就是线程的句柄。如果没有句柄的话,系统就不会知道如何得到线程的状态。但是保持这个线程的句柄不是线程运行的条件。

        关闭线程句柄知识释放句柄资源,新开启的线程之后,如果不在利用该线程的句柄,我们就应该释放掉线程的句柄,释放系统的资源。关闭线程句柄和线程的结束与否没有关系。系统中的句柄资源是有限的。

        对于CloseHandle函数只是减少一个对于hThread的引用计数,用于释放线程的资源,但不是用于终止线程的。终止线程使用的是terminalthread or  exitthread。 TerminateThread的使用会导致各类资源的释放问题,主要是和该线程相连的各种DLL,他们都无法得到线程退出的通知。应该在线程中自然的退出(ExitThread)或是直接调用ExitThread线程,退出的时候应该有同步对象和其他机制实现,主线程一般使用的是WaitForSingleObject函数等待该线程的句柄,以确保现成的退出。

    例子:

    HANDLE hMutex;

     

    DWORD WINAPI Fun(LPVOID lpParamter){

        for (int i = 0; i < 10; i++){

            WaitForSingleObject(hMutex, INFINITE);

            cout << "Fun display" << i << endl;

            ReleaseMutex(hMutex);

            Sleep(1000);

        }

        return 0;

    }

     

    int main(){

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

        hMutex = CreateMutex(NULL, FALSE, "screen");

        for (int i = 0; i < 10; i++){

            WaitForSingleObject(hMutex, INFINITE);

           

            cout << "main thread run " <<i <<  endl;

            //Sleep(1000);

            ReleaseMutex(hMutex);

        }

        cout << endl;

        WaitForSingleObject(hThread, INFINITE);//等待分线程的退出机制

        CloseHandle(hThread);

        CloseHandle(hMutex);

        return 0;

    }

    追梦的飞飞

    于广州中山大学 201310221

    HomePage: http://yangtengfei.duapp.com

  • 相关阅读:
    【Java学习笔记】<集合框架>Iterator的子接口ListIterator
    【Java学习笔记】<集合框架>List特有的取出方式之一
    【Java学习笔记】集合框架Ⅱ
    【Java学习笔记】集合框架Ⅰ
    【PS】Ⅱ图像合成与渐变工具笔记
    【PS】Ⅰ基础及选框工具笔记
    [PS]简单的智能电视制作案例
    Spring中线程池的使用
    SpringBoot 多线程
    solr DIH 设置定时索引
  • 原文地址:https://www.cnblogs.com/hbhzsysutengfei/p/3409481.html
Copyright © 2020-2023  润新知