当前位置:C++技术网 > 资讯 > 线程学习心得浅析

线程学习心得浅析

更新时间:2015-09-02 08:03:18浏览次数:1+次

    

 我们多少听过“进程”,”线程“这样的词语,这是计算机上的一些专业术语,进程顾名思义就是正在进行的程序,而线程则是计算机上的最小执行单位,计算机执行进程时本质上是在执行线程(这是个人的理解)。因此我们这篇文章重点讲解下线程。

    我最开始接触线程是在孙鑫的MFC视频中,当时听得迷迷糊糊的,特别难懂,尤其是讲到进程同步的实现利用到的那些同步函数,那可真是煎熬啊。由于不懂,就只能百度,可是百度也不详细,就问人咯,别人讲的也不清楚,于是,一直云雾里;我们学习过程中会遇到很多的困难,我们需要人的帮助,讲解,我特别理解这种心情,因此,在学习过程中,我把自己的学习心得记录下来,与君共勉,希望多少能帮上你。

    上正题:我们使用线程自然需要创建线程,要不然哪来的线程?来!看代码:

    

HANDLE CreateThread(

LPSECURITY_ATTRIBUTES lpThreadAttributes,

DWORD dwStackSize,

LPTHREAD_START_ROUTINE lpStartAddress,

LPVOID lpParameter,

DWORD dwCreationFlags,

LPDWORD lpThreadId

);


 

    lpThreadAttributes 描述施行于这一新线程的 security 属性。NULL 表
    示使用缺省值。此参数在 Windows 95 中被忽略。
    dwStackSize 新线程拥有自己的堆栈。0 表示使用缺省大小:
    1MB。
    lpStartAddress 新线程将开始的起始地址。这是一个函数指针。(译
    注:在 C 语言中函数名称即代表函数指针,所以这里可放一个函数名称)
    lpParameter 此值将被传送到上述所指定之新线程函数去,作为参数。
    dwCreationFlags 允许你产生一个暂时挂起的线程。默认情况是“立即开始执行”。
    lpThreadId 新线程的 ID 会被传回到这里。
    返回值:
    如果 CreateThread( ) 成功,传回一个 handle,代表新线程。否则传回一个FALSE。如果失败,你可以调用 GetLastError( )获知原因。
    记住,如果函数参数有缺省值,我们最好使用它。

    知道吗?刚开始接触线程时,我把C语言中的函数调用与线程函数的使用完全搞混了,以为两者就是一个意思,其实不是,来我们看个对比:

    

#include <stdio.h>

int square(int n);

void main( )

{

int result; (1)

result = square(5); (2)

printf("%d\n", result); (5)

}

int square(int n)

{

int product = n * n; (3)

return product; (4)

}

#include <windows.h>

DWORD WINAPI ThreadFunc(LPVOID);

void main( )

{

HANDLE hThread; (1)

DWORD threadId;

hThread = CreateThread(NULL,

0,

ThreadFunc,

0,

0,

&threadId ); (2)

printf("Thread running");(4)

}

DWORD WINAPI ThreadFunc(LPVOID p)

{

// ... (3)

}


 

面对一个函数调用操作,控制权会转移到被调用函数中,执行完毕后再返回原调用处。为了印出平方值,square( )必须回到 main( )。多线程范例中,我们不直接调用 ThreadFunc( ),我们调用的是 CreateThread( )并交给它 ThreadFunc( )地址。 CreateThread( )开启一个新的线程,该线程调用 ThreadFunc( )。而原来的线程继续前进。换句话说,ThreadFunc()被异步地 (asynchronously)执行了,意思是 ThreadFunc( )不需要在 main( )继续前进之前完成。所以,返回值也就不可能像传统方式那样。
    接下来我们看个程序,来说说,线程的一些”秘密“:
    

#include "windows.h"

#include "stdio.h"

 

DWORD WINAPI myfun1(LPVOID lpParameter);

DWORD WINAPI myfun2(LPVOID lpParameter);

 

int main()

{

           HANDLE handle1,handle2;

           handle1=CreateThread(NULL,0,myfun1,NULL,0,NULL);

           printf("线程1开始运行!\n");

           handle1=CreateThread(NULL,0,myfun2,NULL,0,NULL);

           printf("线程2开始运行!\n");

           ::CloseHandle(handle1);

           ::CloseHandle(handle2);

           while(1)

           {

                       if(getchar()=='q')

                       {

                                   return 0;

                       }

                       else{

                                   ::Sleep(3000);

                       }

           }

          

}

DWORD WINAPI myfun1(LPVOID lpParameter)

{

           printf("线程1正在运行!\n");

//        ::Sleep(3000);

           return 0;

}

DWORD WINAPI myfun2(LPVOID lpParameter)

{

           printf("线程2正在运行!\n");

//        ::Sleep(3000);

           return 0;

}

     

    

每次的运行结果都不一样,为什么?这是因为“时间切片”,你可以试试,我们运行程序后,它得结果会不一样,与我们用函数调用的思想来解释完全不同,这是因为,我们的计算机会给我们的线程分配时间切片,也就是一个小程序运行的时间限制,所以我们每次运行的结果不一样,具体的关于线程时间片的解说,请到本站的资源下载---->书籍中下载侯俊杰的《深入浅出MFC》。

 

相信讲到现在你也懂了点,现在我们开始下面的讲解---线程同步。线程同步是指统一进程中的多个线程互相协调工作达到一致性。通俗点说就是,因为时间片的存在,导致现成的执行也不确定,因此我们引入线程同步函数,使得当一个线程程序,其他的线程程序则处于等待状态。线程同步我们可以用四种函数来实现:

    事件(events)
    信号量(semaphores)
   互斥器(mutexes)
   管道(Pipes。分为 named 和 anonymous 两种)

 

不过我在这里只讲解互斥器:

HANDLE CreateMutex(

LPSECURITY_ATTRIBUTES lpMutexAttributes, 安全属性结构指针,可为NULL,表示默认安全性

BOOL bInitialOwner, //拥有互斥对象的线程(一般是主线程)是否占有该互斥量,TRUE:占有,FALSE:不占有

LPCTSTR lpName //设置互斥对象的名字

);

 

 

首先创建互斥对象。我们写一个程序,上代码:

    

#include <windows.h>                                                                             //包含头文件

#include <stdio.h>

 

DWORD WINAPI myfun1(LPVOID lpParameter);//声明线程函数

DWORD WINAPI myfun2(LPVOID lpParameter);

HANDLE hmutex;

int a=0;                                                                            //定义全局变量a

 

int main()

{        

           hmutex=::CreateMutex(NULL,FALSE,NULL);                           //创建互斥对象并返回其句柄

           HANDLE h1,h2;                                                                                 //定义线程句柄

           h1=::CreateThread(NULL,0,myfun1,NULL,0,NULL);      //创建线程1

           printf("线程1开始运行!\r\n");

           h2=::CreateThread(NULL,0,myfun2,NULL,0,NULL);      //创建线程2

           printf("线程2开始运行!\r\n");

           ::CloseHandle(h1);                                                                //关闭线程句柄对象

           ::CloseHandle(h2);

           ::Sleep(10000);                                                                       //程序睡眠10秒

           return 0;

}

 

DWORD WINAPI myfun1(LPVOID lpParameter)                      //线程函数1

{

           while(1)

           {

                       ::WaitForSingleObject(hmutex,INFINITE);                  //请求互斥对象

                       if(a<10000)

                       {

                                   a+=1;                                                                                        //变量自加

                                   ::Sleep(1000);                                                                             //线程睡眠1秒

                                   printf("线程1:%d\r\n",a);

                                   ::ReleaseMutex(hmutex);                                              //释放互斥对象句柄

                       }

                       else

                       {

                                   ::ReleaseMutex(hmutex);                                        //释放互斥对象句柄

                                   break;                                                                                                       

                       }

           }

    return 0;                                                                                          

}

DWORD WINAPI myfun2(LPVOID lpParameter)                      //线程函数2

{

           while(1)

           {

                       ::WaitForSingleObject(hmutex,INFINITE);                  //请求互斥对象

                       if(a<10000)

                       {

                                   a+=1;

                                   ::Sleep(1000);

                                               printf("线程2:%d\r\n",a);                        //输出变量

                                   ::ReleaseMutex(hmutex);                                        //释放互斥对象句柄

                       }

                       else

                       {          

                                   ::ReleaseMutex(hmutex);                                         //释放互斥对象句柄

                                   break;                                                                                      //跳出循环

                       }

           }

    return 0;                                                                                          //线程正常退出

}

 

waitforsingleobject(....)函数就是用来等待线程,如果该线程没有信号,就不能激活waitforsingleobject(),就会卡在线程这里,不会在往下执行,直到接收到信号,我们看下代码吧:

    

DWORD WINAPI WaitForSingleObject(

__in HANDLE hHandle,

__in DWORD dwMilliseconds

);

     

hHandle[in]对象句柄。可以指定一系列的对象,如Event、Job、Memory resource notification、Mutex、Process、Semaphore、Thread、Waitable timer等。

dwMilliseconds[in]定时时间间隔,单位为milliseconds(毫秒).如果指定一个非零值,函数处于等待状态直到hHandle标记的对象被触发,或者时间到了。如果dwMilliseconds为0,对象没有被触发信号,函数不会进入一个等待状态,它总是立即返回。如果dwMilliseconds为INFINITE,对象被触发信号后,函数才会返回。

 

    接下来就是释放互斥对象,我们创建了互斥对象,最后得释放它,要不然你霸着它不放,别人怎么用,对不对?所以我们释放互斥对象,其实还有更深入的原因,每当我们创建互斥对象时,就相当于给它自增一,释放它就等于自减一,为什么会自增自减呢?我们不做讨论,有兴趣的,可以看看孙鑫的MFC视频,详细了解。

    可能你觉得我写这个代码没什么用,其实我们现在学的任何东西以后都能用上,我们可以用线程的同步来实现聊天程序,还有其他用途哦!这就不详述了。以后我们会用到的.......

 在这里,我强烈推荐《win32多线程程序设计》《windows核心编程》都是对线程的详细描述,本网站提供下载,路径同上,欢迎哦!!!!