进程与线程

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

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

 

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

 

 

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

新建一个基于对话框的应用程序,在主对话框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);
    }
    
}

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

 

 

 

 

 
posted @ 2012-05-25 17:33  CBDoctor  阅读(1211)  评论(0编辑  收藏  举报