多线程学习-线程基本概念及线程创建--发布日期:2008-07-16 23:19

来源:互联网 发布:淘宝 女装的退款率 编辑:程序博客网 时间:2024/06/04 01:04
       以前上操作系统时学过一些关于进程,线程以及同步的理论,也在Linux做过一些多线程的实验,但是感觉那些都只是为了应付课程之类的东东,今天看了孙鑫老师的VC视频第15讲,主要讲的线程的创建以及使用互斥对象进行线程同步操作,感觉他这套视频确实是很经典。下面总结一下学习的东西:

程序是计算机指令的集合,它以文件的形式存储在磁盘上。

进程:通常被定义为一个正在运行的程序的实例,是一个程序在其自身的地址空间中的一次执行活动。进程是资源申请、调度和独立运行的单位,因此,它使用系统中的运行资源;而程序不能申请系统资源,不能被系统调度,也不能作为独立运行的单位,因此,它不占用系统的运行资源。

进程由两个部分组成

1、操作系统用来管理进程的内核对象。内核对象也是系统用来存放关于进程的统计信息的地方。

2、地址空间。它包含所有可执行模块或DLL模块的代码和数据。它还包含动态内存分配的空间。如线程堆栈和堆分配空间。

进程是不活泼的。进程从来不执行任何东西,它只是线程的容器。若要使进程完成某项操作,它必须拥有一个在它的环境中运行的线程,此线程负责执行包含在进程的地址空间中的代码。单个进程可能包含若干个线程,这些线程都“同时” 执行进程地址空间中的代码。每个进程至少拥有一个线程,来执行进程的地址空间中的代码。当创建一个进程时,操作系统会自动创建这个进程的第一个线程,称为主线程。此后,该线程可以创建其他的线程。

线程由两个部分组成

1、线程的内核对象,操作系统用它来对线程实施管理。内核对象也是系统用来存放线程统计信息的地方。

2、线程堆栈,它用于维护线程在执行代码时需要的所有参数和局部变量。

当创建线程时,系统创建一个线程内核对象。该线程内核对象不是线程本身,而是操作系统用来管理线程的较小的数据结构。可以将线程内核对象视为由关于线程的统计信息组成的一个小型数据结构。

线程总是在某个进程环境中创建。系统从进程的地址空间中分配内存,供线程的堆栈使用。新线程运行的进程环境与创建线程的环境相同。因此,新线程可以访问进程的内核对象的所有句柄、进程中的所有内存和在这个相同的进程中的所有其他线程的堆栈。这使得单个进程中的多个线程确实能够非常容易地互相通信。

线程只有一个内核对象和一个堆栈,保留的记录很少,因此所需要的内存也很少。

因为线程需要的开销比进程少,因此在编程中经常采用多线程来解决编程问题,而尽量避免创建新的进程。

操作系统为每一个运行线程安排一定的CPU时间 —— 时间片。系统通过一种循环的方式为线程提供时间片,线程在自己的时间内运行,因时间片相当短,因此,给用户的感觉,就好像线程是同时运行的一样。如果计算机拥有多个CPU,线程就能真正意义上同时运行了。

创建线程主要使用API函数CreateThread,在MSDN中的原型声明为:

HANDLE CreateThread(

  LPSECURITY_ATTRIBUTES lpThreadAttributes,

  SIZE_T dwStackSize,

  LPTHREAD_START_ROUTINE lpStartAddress,

  LPVOID lpParameter,

  DWORD dwCreationFlags,

  LPDWORD lpThreadId

);

第一个参数为线程安全属性,一般设置为NULL就可以了;第二个参数是线程使用的堆栈大小,设为0就可以了,表示使用默认的;第三个参数即是线程的起始地址,也就是线程函数;第四个参数是传递给线程函数的参数,一般都会经过case的;第五个参数是线程创建标志,若为CREATE_SUSPENDED表示线程创建后被挂起,需要通过ResumeThread唤醒之,一般设为0表示马上执行;最后一个参数是一个传出参数,表示线程ID。

可以通过ExitThread来显示终止线程,注意这个函数需要传递线程ID,所以在创建线程时就要记录线程ID,通过CloseHandle可以递减分配给线程的所有内核对象计数。

下面通过视频中的火车站售票系统来说明如何使用CreateThread创建线程,这个例子确实很实用:

 

  1. #include <windows.h>
  2. #include <iostream>
  3. using namespace std;
  4. DWORD WINAPI Seller1(LPVOID lpParameter);
  5. DWORD WINAPI Seller2(LPVOID lpParameter);
  6. int numberTicket=100;
  7. int main()
  8. {
  9.     
  10.     HANDLE sellThread1;
  11.     
  12.     HANDLE sellThread2;
  13.     
  14.     sellThread1=CreateThread(NULL,0,Seller1,NULL,0,0);
  15.     
  16.     sellThread2=CreateThread(NULL,0,Seller2,NULL,0,0);
  17.     
  18.     CloseHandle(sellThread1);
  19.     
  20.     CloseHandle(sellThread2);
  21.     
  22.     Sleep(4000);
  23.     return 0;   
  24. }
  25. DWORD WINAPI Seller1(LPVOID lpParameter)
  26. {
  27.     
  28.     while(true)     
  29.     {       
  30.         if(numberTicket>0)     //票还没售完          
  31.         {           
  32.             cout<<"Seller1 sell NO."<<numberTicket--<<" ticket."<<endl;
  33.             
  34.         }       
  35.         else            
  36.             break;      
  37.     }   
  38.     return 0;   
  39. }
  40. DWORD WINAPI Seller2(LPVOID lpParameter)
  41. {
  42.     while(true)     
  43.     {
  44.         if(numberTicket>0)     //票还没售完          
  45.         {           
  46.             cout<<"Seller2 sell NO."<<numberTicket--<<" ticket."<<endl;         
  47.         }       
  48.         else            
  49.             break;      
  50.     }   
  51.     return 0;   
  52. }

代码很简单,需要注意的就是CloseHandle并没有终止该线程,只是使该线程的内核对象计数减少

输入结果截图:

由结果可以看出输出是又问题的,这是因为线程在执行过程中没有进行同步而引起的,后面再介绍解决这个问题的方法。

原创粉丝点击