首页 > 编程语言 > 详细

操作系统之Windows多线程简单学习

时间:2015-10-15 18:57:13      阅读:190      评论:0      收藏:0      [点我收藏+]
  1. 操作系统课程学习过程中,.................  先了解下什么是句柄。

          句柄是一种指向指针的指针。我们知道,所谓指针是一种内存地址。应用程序启动后,组成这个程序的各对象是住留在内存的。如果简单地理解,似乎我们只要获知这个内存的首地址,那么就可以随时用这个地址访问对象。但是,如果您真的这样认为,那么您就大错特错了。我们知道,Windows是一个以虚拟内存为基础的操作系统。在这种系统环境下,Windows内存管理器经常在内存中来回移动对象,依此来满足各种应用程序的内存需要。对象被移动意味着它的地址变化了。如果地址总是如此变化,我们该到哪里去找该对象呢?
         为了解决这个问题,Windows操作系统为各应用程序腾出一些内存储地址,用来专门登记各应用对象在内存中的地址变化,而这个地址(存储单元的位置)本身是不变的。Windows内存管理器在移动对象在内存中的位置后,把对象新的地址告知这个句柄地址来保存。这样我们只需记住这个句柄地址就可以间接地知道对象具体在内存中的哪个位置。这个地址是在对象装载(Load)时由系统分配给的,当系统卸载时(Unload)又释放给系统。

  2. 句柄地址(稳定)→记载着对象在内存中的地址→对象在内存中的地址(不稳定)→实际对象

  3. 线程与句柄的关系:

           句柄可以认为是系统对资源(如线程)的分配的一个编号。关闭这个编号,对于不同的资源,效果不尽相同。对于线程来说,关闭这个编号并不意味着终止线程,只是之后很难再操纵这个线程。

  4.  线程句柄与线程ID的区别:

                 CreateThread() API 用于创建线程。 API 返回同时线程句柄和线程标识符 (ID)。线程句柄有完全访问权创建线程对象。 运行线程时线程 ID 唯一标识线程在系统级别。

            1)  ID是在Windows系统范围内唯一标示Thread的。  

            2)  Handle是用来操作Thread的,可以有多个,每个HANDLE可以有不同的操作权限,在不同进程OpenThread得到的值不一样。 

            3)  线程的ID是系统全局的,其HANDLE是进程局部的.

            4)  HANDLE是os和client之间用来操作进程和线程一个桥梁,os有一张维护HANDLE的表单,里面大概放置了   HANDLE的引用计数和有关的属性,HANDLE是os标识进程和线程的东西,但是用户也可以用这个来标识进程和线程,对其操作;而ID是os用来标识进程和线程的,并且是全局唯一的,  但用户可以通过这个ID获得进程线程的HANDLE,多次得到的HANDLE并不一定是一样的.HANDLE是内核对象,而ID好像不是,并没有专门创建ID的函数。

            5)  ID是CreateThread时操作系统自动生成的。

            6)  线程的句柄和id是不同的。 
                      在windows系统中,线程的id是唯一对应的,也就是说,如果两个线程返回相同的id,则他们必然是同一线程,反之一定是不同的线程。而线程的句柄并不是线程的唯一标识,线程的句柄只是用来访问该线程的的一个32位值,尽管相同的句柄一定标识同一线程,但同一线程可能拥有两个打开的句柄,因此,不能用句柄来区分两个线程是否是同一线程。

 

 

当线程终止运行时,会发生下列操作:
  1.  线程拥有的所有用户对象均被释放。在 Windows 中,大多数对象是由包含创建这些对象的线程的进程拥有的。但是一个线程拥有两个用户对象,即窗口和挂钩。当线程终止运行时,系统会自动撤消任何窗口,并且卸载线程创建的或安装的任何挂钩。其他对象只有在拥有线程的进程终止运行时才被撤消。


  2.  线程的退出代码从 STILL_ACTIVE 改为传递给 ExitThread 或 TerminateThread 的代码。
  3.  线程内核对象的状态变为已通知。
  4.  如果线程是进程中最后一个活动线程,系统也将进程视为已经终止运行。
  5.   线程内核对象的使用计数递减 1。
  当一个线程终止运行时,在与它相关联的线程内核对象的所有未结束的引用关闭之前,该内核对象不会自动被释放。


  一旦线程不再运行,系统中就没有别的线程能够处理该线程的句柄。然而别的线程可以调用 GetExitcodeThread 来检查由 hThread 标识的线程是否已经终止运行。如果它已经终止运行,则确定它的退出代码:

// 退出代码的值在 pdwExitCode 指向的 DWORD 中返回。如果调用 GetExitCodeThread 时线程尚未终止运行, 
 //该函数就用 STILL_ACTIVE 标识符(定义为 0x103)填入 DWORD。如果该函数运行成功,便返回 TRUE。 
 
BOOL GetExitCodeThread(HANDLE hThread, PDOWRD pdwExitCode);

   
    线程退出的时候内核对象就会被激发, WaitForSingleObject()为堵塞函数,等待线程的内核对象被激发。

    所以终止线程并释放句柄对象的顺序是:

                            TerminateThread()-->WaitForSingleObject()-->CloseHandle().

    //线程、线程句柄、线程ID的生成和消失

    hThread = CreateThread(NULL, 0, ThreadProc, NULL, 0, &dwId); //至此,新线程,线程句柄,线程ID产生

    TerminateThread(hThread, 0); //至此,线程ID,线程句柄都依然存在

    WaitForSingleObject(hThread, INFINITE); //至此,线程本身和线程ID消失
 
    CloseHandle(hThread);  //至此,线程句柄消

    注:

    DWORD WaitForSingleObject( HANDLE hHandle, DWORD dwMilliseconds);
    参数:
            hHandle[in]对象句柄。可以指定一系列的对象,如Event、Job、Memory resource notification、Mutex、Process、Semaphore、Thread、Waitable timer等。

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

            // 2015-10-15
// 创建两个线程简单打印  hello

#include<windows.h>
#include<iostream.h>
 
// 定义两个线程函数Write()和Read()
// Read()、Write()函数分别使用VC++的P、V操作WaitForSingleObject()和ReleaseSemaphore()
DWORD WINAPI Write(LPVOID lpParameter);
DWORD WINAPI Read(LPVOID lpParameter);
 
// 缓冲区资源
unsigned char buffer;
 
// 两个信号量,分别使用缓冲区buffer的两个资源:满和空
// semaphore1 表示缓冲区buffer满,初始值为0,最大值为1
// semaphore2 表示缓冲区buffer空,初始值为1,最大值为1
HANDLE semaphore1;
HANDLE semaphore2;
 
//线程句柄
 HANDLE hThread1;
 HANDLE hThread2;
 
void main(){  
    // buffer清空
    buffer=‘ ‘;
    // 创建信号量
    semaphore1=CreateSemaphore(NULL,0,1,NULL);
    semaphore2=CreateSemaphore(NULL,1,1,NULL);
 
    // 创建线程CreateThread函数创建一个线程后, 系统内部拥有一个该线程的句柄, 同时返回一个句柄给用户.           hThread1=CreateThread(NULL,0,Write,NULL,0,NULL);
    hThread2=CreateThread(NULL,0,Read,NULL,0,NULL);
 
    // 在子线程执行完前主线程不能退出,否则显示结果异常
    Sleep(10000);
 
    // 关闭句柄   CloseHandle(HANDLE h)关闭该句柄,防止因遗忘而疏忽关闭它,造成内存泄漏。          
    CloseHandle(Write);
    CloseHandle(Read);
 
    // 释放信号量
    CloseHandle(semaphore1);
    CloseHandle(semaphore2);
}

//write 负责每次向缓冲区中写入一个字符  而read负责每次从缓冲区中取出一个字符 
 
// 线程1对应的p函数实体:缓冲区buffer写操作 
DWORD WINAPI Write(LPVOID lpParameter){
    unsigned char str[6]="Hello";
    int i=0;
    for (i=0;i<5;i++){
        // 等待缓冲区空
        WaitForSingleObject( semaphore2, INFINITE );
        // 缓冲区空时,向缓冲区写入数据
        buffer=str[i];
        // 通知缓冲区满
        ReleaseSemaphore( semaphore1, 1, NULL );
    }
    return 0;
}
 
// 线程2对应的V函数实体:缓冲区buffer读操作
DWORD WINAPI Read(LPVOID lpParameter){
    int i=1;
    unsigned char c;
    for (i=1;i<=5;i++){
        // 等待缓冲区满
            // 第二个参数为为 INFINITE,表示对象被触发信号后,函数才会返回。 
        WaitForSingleObject( semaphore1, INFINITE);
        // 从缓存buffer获取数据
        c=buffer;
        // 输出从缓冲区buffer取出的数据
        cout<<c;
        // 通知缓冲区空
        ReleaseSemaphore( semaphore2, 1, NULL );
    }
    // 回车换行
    cout<<endl;
    return 0;  
}

操作系统之Windows多线程简单学习

原文:http://my.oschina.net/u/2405367/blog/517689

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!