• 【多线程学习】之一、线程简介


    线程是程序执行的基本原子单位,是进程的一个实体,是CPU调度和分派的基本单位。一个进程可以由多个线程组成。每个线程都有自己的寄存器组,堆栈,输出机制和一个私有消息队列。

    多线程可以实现并行的处理,避免了某一项任务长时间的占用CPU的时间,从而导致了其他线程闲置的情况。我们在进行多线程编程的时候要注意,当两个线程优先级非常高的时候,他们抢夺CPU的控制权,在线程切换的时候会消耗很多的CPU资源,会降低系统的性能。

    先来看一下创建线程函数:

    HANDLE CreateThread()
    {
    	LPSECURITY_ATTRIBUTES LPThreadAttributes,  //指向SECURITY_ATTRIBUTES的指针
    	SIZE_T dwStackSize,                        //表示线程为自己所用堆栈分配的地址空间的大小 系统缺省值为0
    	LPTHREAD_START-TOUTINE lpStartAddress,     //表示新线程开始执行时代码所在函数的地址 即线程函数名
    	LPVOID lpParameter,                        //是传入线程函数的参数
    	DWORD dwCreationFlags,                     //指定控制线程创建的附加标志 取0线程立即执行 取CREATE_SUSPENDED线程挂起
    	LPDWORD lpThreadld                         //是个DWORD类型的地址,返回赋给该新线程的ID
    }
    

    线程函数lpParameter必须有以下原形:
    
    DWORD WINAPI XXXThreadFun(LPVOID lpParameter)
    {
    	return(0);
    }
    


    下面我们来创建一个线程:

    #include <windows.h>
    #include <stdio.h>
    DWORD WINAPI ThreadFunc( LPVOID lpParam )                         //线程函数,跟普通的函数没什么两样
    { 
        printf( "Parameter = %d.", *(DWORD*)lpParam ); 
        return 0; 
    } 
     
    VOID main( VOID ) 
    { 
        DWORD dwThreadId, dwThrdParam = 1; 
        HANDLE hThread; 
        hThread = CreateThread( NULL,0,ThreadFunc,&dwThrdParam, 0,&dwThreadId); 
       if (hThread == NULL) 
       {
          printf( "CreateThread failed (%d)\n", GetLastError() ); 
       }
       else 
       {
          _getch();
          CloseHandle( hThread );
       }
    }
    


    当我们使用线程的时候要注意,最好的方式是让线程自然结束。也可以人工的调用TerminateThread函数和ExitThread函数来结束线程,但是当我们用这种方式结束线程的时候,系统不会释放线程使用的堆栈,所以建议大家在编程的时候尽量让线程自己退出。

    当我们调用函数退出线程的时候,可能会导致一下几种问题:

    1、目标线程拥有临界区,那么windows系统就不会释放临界区。

    2、如果线程正在执行某种内核调用,那么该线程的进程内核状态可能不合理。

    3、如果目标线程正在操纵共享动态链接库的全局状态,那么windows系统可能破坏动态链接库的状态,从而影响该动态链接库的其他使用者。



    操作系统在创建线程的时候有6个步骤,分别是:

    1、分配线程内核对象标识和管理新创建的线程,内核对象保存很多系统信息来管理该线程,程内核对象的句柄是CreateThread函数的返回值。

    2、把线程的退出代码初始化为STILL_ACTIVE,并把线程的挂起计数设为1.

    3、为新线程分配CONTEXT结构。

    4、通过保留地址空间区域、为该区域提交两页物理存储器、把提交的存储器的保护标志设为PAGE_READWRITE以及把第二页到顶部的页设为PAGE_GUARD属性来准备线程的堆栈。

    5、把LpStartAddr和LopvThread的值放入堆栈的顶部,新线程将其看成传给StartofThread函数的参数。

    6、初始化线程的CONTEXT结构中的堆栈指针寄存器,把它指向上步中windows系统放入堆栈的值,然后操作系统初始化执行指针寄存器,使其指向内部的函数,在windows系统执行线程启动函数的第一条执行前执行内部函数。


    在上文中,我们一直说线程堆栈,线程堆栈,到底线程堆栈是如何确定其大小的呢?

    当我们调用CreateThread函数的时候,在进程的内存地址空间中创建线程的堆栈,在函数中可以指定堆栈的大小,当创建线程以后就不能安全的改变线程的大小了,需要动态的向下增长堆栈。




    2012/10/1

    jofranks 于南昌

  • 相关阅读:
    【机器学习实战】第12章 使用FP-growth算法来高效发现频繁项集
    【机器学习实战】第11章 使用 Apriori 算法进行关联分析
    【机器学习实战】第 10 章 K-Means(K-均值)聚类算法
    【机器学习实战】第9章 树回归
    【机器学习实战】第8章 预测数值型数据:回归
    【机器学习实战】第7章 集成方法 ensemble method
    【机器学习实战】第6章 支持向量机
    学习计划
    第二次作业
    第一次作业
  • 原文地址:https://www.cnblogs.com/java20130723/p/3211395.html
Copyright © 2020-2023  润新知