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

线程是程序执行的基本原子单位,是进程的一个实体,是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 于南昌

原文地址:https://www.cnblogs.com/java20130723/p/3211395.html