线程学习总结-----/*自己编写*/

时间:2022-03-07 08:11:53

一、 WaitForSingleObject()函数

线程学习总结-----/*自己编写*/


二、 _beginthreadex()和CreatThread()区别

       使用标准C运行库函数是,尽量使用_beginthreadex()函数。

       参考:http://blog.csdn.net/morewindows/article/details/7421759


三、 Interlocked系列函数

        多线程环境下对一个全局变量进行读写时,对这个变量的操作必须是原子操作,即一个线程对其进行访问修改时,其它线程要等这个线程操作完毕后才能进行修改。对变量进行原子操作可使用interlocked系列函数,包括自增、自减、赋值等操作。

        参考:http://blog.csdn.net/morewindows/article/details/7429155


四、同步与互斥:

        同步:主线程等待子线程某个操作完成后再启动下一个动作,这就涉及到线程间的同步;

        互斥:子线程之间会互斥的改动和输出全局变量,而要求全局变量规律的改变,这就涉及到线程间的互斥。

        参考:http://blog.csdn.net/morewindows/article/details/7442639

                   http://blog.csdn.net/morewindows/article/details/7445233

        

        解决互斥可以使用interlocked系列函数、也可以使用关键段。

        但这两个都解决不了同步问题,同步可以通过事件来完成。

        如果线程间的互斥只涉及到一个全局变量,可以通过Interlocked系列函数来解决;如果线程间的互斥是一段操作,则需要使用关键段来解决。

        例:参考中的经典多线程例子里,线程函数如下:

//子线程函数  
unsigned int __stdcall Fun(void *pPM)
{
int nThreadNum = *(int *)pPM;
SetEvent(g_hThreadEvent);


Sleep(50);//some work should to do


EnterCriticalSection(&g_csThreadCode);//进入各子线程互斥区域
g_nNum++;
Sleep(0);//some work should to do
printf("线程编号为%d 全局资源值为%d\n", nThreadNum, g_nNum);
LeaveCriticalSection(&g_csThreadCode);//离开各子线程互斥区域
return 0;
}

主函数如下:

// MulThread.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#include <stdio.h>
#include <Windows.h>
#include <process.h>

long g_nNum; //全局资源
unsigned int __stdcall Fun(void *pPM); //线程函数
const int THREAD_NUM = 10; //子线程个数
//关键段变量声明
CRITICAL_SECTION g_csThreadCode;
HANDLE g_hThreadEvent;

int _tmain(int argc, _TCHAR* argv[])
{
printf(" 经典线程同步 关键段\n");
printf(" -- by MoreWindows( http://blog.csdn.net/MoreWindows ) --\n\n");

//关键段初始化
InitializeCriticalSection(&g_csThreadCode);
g_hThreadEvent = CreateEvent(NULL,FALSE,FALSE,NULL);

HANDLE handle[THREAD_NUM];
g_nNum = 0;
int i = 0;
while (i < THREAD_NUM)
{
handle[i] = (HANDLE)_beginthreadex(NULL, 0, Fun, &i, 0, NULL);
WaitForSingleObject(g_hThreadEvent,INFINITE);
i++;
}
WaitForMultipleObjects(THREAD_NUM, handle, TRUE, INFINITE);

DeleteCriticalSection(&g_csThreadCode);
CloseHandle(g_hThreadEvent);

system("pause");
return 0;
}


如果,不需要输出printf这句,直接用interlocked函数InterlockedIncrement((LPLONG)&g_nNum),代替g_nNum++即可解决互斥问题。但要求是要有输出这句,printf语句中因又用到了g_nNum这个全局变量,所以如果只是用Interlocked函数,

//子线程函数  
unsigned int __stdcall Fun(void *pPM)
{
int nThreadNum = *(int *)pPM;
SetEvent(g_hThreadEvent);

Sleep(50);//some work should to do

//EnterCriticalSection(&g_csThreadCode);//进入各子线程互斥区域
//g_nNum++;
InterlockedIncrement((LPLONG)&g_nNum);
Sleep(0);//some work should to do
printf("线程编号为%d 全局资源值为%d\n", nThreadNum, g_nNum);
//LeaveCriticalSection(&g_csThreadCode);//离开各子线程互斥区域
return 0;
}

输出结果如下:

线程学习总结-----/*自己编写*/

可以看到,全局变量的输出值并没有依次递增,这是因为printf这句并不是紧接在InterlockedIncrement((LPLONG)&g_nNum)后执行,比如线程3执行完InterlockedIncrement((LPLONG)&g_nNum)后,还没执行后面的printf,线程5开始调用Fun,进入到printf这句,此时的g_nNum仍存在互斥。

为解决此问题,应用关键段,将有互斥的语句全包含进来,这样可以解决互斥问题。

如下:

EnterCriticalSection(&g_csThreadCode);//进入各子线程互斥区域  
g_nNum++;
Sleep(0);//some work should to do
printf("线程编号为%d 全局资源值为%d\n", nThreadNum, g_nNum);
LeaveCriticalSection(&g_csThreadCode);//离开各子线程互斥区域
线程函数按上述修改后,输出如下:

线程学习总结-----/*自己编写*/


总结:

单个全局变量的互斥可使用Interlocked系列函数完成原子操作,一段互斥的操作可使用关键段;

关键段是有“线程所有权”概念的;

关键段可以用于线程间的互斥,但不可以用于同步;

事件可以解决线程间同步问题,因此也能解决互斥问题

五、 互斥量

        互斥量是内核对象,它与关键段都有“线程所有权”所以不能用于线程的同步。

         互斥量能够用于多个进程之间线程互斥问题,并且能完美的解决某进程意外终止所造成的“遗弃”问题。

        参考:http://blog.csdn.net/morewindows/article/details/7470936