• 进程与线程


    我们可以把进程看作是一个正在独立运行的独立的程序,在内存中有其完备的数据空间和代码空间。

    线程是在某一个进程中单独运行的单元,也就是说,线程存在于进程之中。一个进程有多个或者单个线程组成,各个线程共享相同的代码和全局数据,但是各有其自己的堆栈。由于每一个线程有一个堆栈,所以局部变量对每一个线程来说是私有的。由于所有的线程共享相同的代码和全局数据,它们比进程更紧密,比单独的进程间更趋向于相互作用。

    一个进程与线程的最重要的区别是:线程拥有自己的全局数据。线程存在于进程中,因此一个进程的全局变量有所有的线程共享。

    我们编写一个程序,来说明一下,为什么使用多线程!

    新建一个基于对话框的应用程序,在主对话框IDD_SINGLETHEAD_DIALOD中添加一个Button按钮,设置属性,使它的响应函数为延迟5秒,代码如下:

    void CSingThreadDlg::OnFiveSecondDelag( )
    {
        Sleep(5000);
    }
    

    发现整个对话框处于死机的状态,就是因为单线程状态下,程序只能等待。这时候有必要采用多线程的方法。

    一。怎样创建线程

    创建多线程的函数有很多:(1)全局函数AfxBeginThread( ... );

                                       (2)  CreatThread( ... );

                                      (3)_beginthread( )和_beginthreadex()函数

    我们详细讲解第一个函数:

    MSDN上给出如下定义:

    CWinThread* AfxBeginThread(
       AFX_THREADPROC pfnThreadProc,
       LPVOID pParam,
       int nPriority = THREAD_PRIORITY_NORMAL,
       UINT nStackSize = 0,
       DWORD dwCreateFlags = 0,
       LPSECURITY_ATTRIBUTES lpSecurityAttrs = NULL 
    );
    CWinThread* AfxBeginThread(
       CRuntimeClass* pThreadClass,
       int nPriority = THREAD_PRIORITY_NORMAL,
       UINT nStackSize = 0,
       DWORD dwCreateFlags = 0,
       LPSECURITY_ATTRIBUTES lpSecurityAttrs = NULL 
    );

    各个参数的说明如下:

    返回值: 一个指向新线程的线程对象


    pfnThreadProc : 线程的入口函数,声明一定要如下: UINT MyThreadFunction( LPVOID pParam );
    pParam : 传递入线程的参数,注意它的类型为:LPVOID,所以我们可以传递一个结构体入线程.
    nPriority : 线程的优先级,一般设置为 0 .让它和主线程具有共同的优先级.
    nStackSize : 指定新创建的线程的栈的大小.如果为 0,新创建的线程具有和主线程一样的大小的栈
    dwCreateFlags : 指定创建线程以后,线程有怎么样的标志.可以指定两个值:

              CREATE_SUSPENDED : 线程创建以后,会处于挂起状态,直到调用: ResumeThread
              0 : 创建线程后就开始运行.
    lpSecurityAttrs : 指向一个 SECURITY_ATTRIBUTES 的结构体,用它来标志新创建线程的安全性.如果为 NULL ,那么新创建的线程就具有和主线程一样的安全性.

     

    之所以有两个函数,是因为线程分为两种:用户界面线程、工作者线程(我们将在下一节中介绍)

     

    二.怎样终止一个线程

    终止线程的方法有三种:

    (1)可以在自身内部调用AfxEndThread()来终止自身的运行;

    (2)可以在线程的外部调用Terminate()来强行终止一个线程的运行,然后调用closehandle()来释放线程所占用的堆栈。Terminate()有资源泄漏,因此要慎用这个函数。

    (3)设置一个全局变量,改变这个全局变量,通知线程的执行函数返回,则该线程终止。这种方式通过线程执行函数的返回来终止线程,是一种安全的方式。

     

    三.编写线程函数

    需要注意的是:线程函数必须为一个独立的函数,因而,线程函数通常为全局函数或者静态成员函数。如果该线程函数一定要定义为一个类的成员函数,那么一般要把这个函数声明为静态的。

     

    下面我们用一个实例来说明线程和进程的概念:我们写一个简单的伪代码:

    (1)首先建立一个基于单文档类的MFC程序,不要忘记要支持Winsock类。

    (2)在菜单中添加一个菜单项,并分别添加三个子菜单:启动线程、结束线程、启动进程。

    (3)为启动线程添加消息消息相应函数:

    VOID Cview::OnStartThread( )
    {
        // TODO: Add your command handler code here
        
        flag = false;    //flag 为标志位,用于结束线程。类似于第三种结束线程的方式。
    
        if(numThread<2)  //numThread为允许开启的线程的数目,在这里,我们允许的数目为2个
        {
            numThread++;
            AfxBeginThread(AddPicture,this);//AddPicture 为线程函数
        }
    }

    (3)添加全局的线程函数(如前所述,必须为全局或者静态成员函数,这里我们用全局的函数)

     1 UINT AddPicture(LPVOID p)
     2 {
     3     if (TRUE ==flag)
     4     {    
     5         switch(numThread)
     6         {
     7         case 1:
     8             {            
     9                 AfxMessageBox("线程1启动");//执行功能1
    10                 break;
    11             }
    12         case 2:
    13             {
    14                 AfxMessageBox("线程2启动");;//执行功能2
    15                 break;
    16             }
    17         default:
    18             AfxEndThread(0);//结束进程
    19             //return 0;
    20         }
    21         
    22         
    23     }
    24     else 
    25         AfxEndThread(0);
    26     return 0;
    27 
    28 }

    (4)为“结束线程”添加消息消息相应函数

    void ampleView::IsStop()
    {
    	flag = FALSE;
    	numThread = 0;
    	
    }
    

      

    这样,我们就基本完成了多线程的基本功能,当你点击“运行线程”的菜单项时,就会在客户区出现“线程1运行”的对话框;当你再次点击时,会出现

    “线程2运行”的对话框;当你点击“结束线程”时,就会终止整个线程的运行。

     

    --------------------------------------------------------------------------------------------------------------------------------------------

    下面我们再在上面程序的基础上,打开一个进程,用于加深进程和线程的理解:

     


    再建立一个菜单项,名称为--开启进程

    添加代码如下:

    void Cview::OnCreatePress()
    {
    
        TCHAR currentPath[MAX_PATH];
        GetModuleFileName(NULL,currentPath,MAX_PATH);
    
        PROCESS_INFORMATION pi;//进程信息
        STARTUPINFO si = {sizeof(si} };
        BOOL ret = CreateProcess(NULL,currentPath,NULL,NULL,FALSE,0,NULL,NULL,&si,&pi);
        if(ret)
        {
            //父进程与子进程之间不需要信息的交换,因此可以关闭子进程的主线程句柄和子程序句柄
            CloseHandle(pi.hThread);
            //关闭子进程语句
            CloseHandle(pi.hProcess);
        }
        
    }

    完成!当我们点击开启线程后,就会跳出一个和原界面相同的进程界面。

     

     

     

     
  • 相关阅读:
    Docker 基础 B站 学习 最强 教程
    apache+php安装
    php拓展 swoole 安装
    go beego框架 入门使用 (一)
    php 使用 phpword 操作 word 读取 word
    linux + MongoDB 安装 + 部署 + 讲解 (满满干货看完记得收藏噢)
    Thanos设计简析
    Prometheus TSDB文件格式-index
    Linux Kernel文件系统写I/O流程代码分析(二)bdi_writeback
    Linux Kernel文件系统写I/O流程代码分析(一)
  • 原文地址:https://www.cnblogs.com/CBDoctor/p/2518396.html
Copyright © 2020-2023  润新知