windows下多线程同步(利用事件对象,互斥对象,关键代码段)实现

一:利用事件实现线程同步

1.createthread函数的用法

hThread = CreateThread(&security_attributes, dwStackSize, ThreadProc,pParam, dwFlags, &idThread) ;

HANDLE CreateThread(

LPSECURITY_ATTRIBUTES lpThreadAttributes,

DWORD dwStackSize,             

LPTHREAD_START_ROUTINE lpStartAddress,   

LPVOID lpParameter,            

DWORD dwCreationFlags,           

LPDWORD lpThreadId           

);

第一个参数是指向SECURITY_ATTRIBUTES型态的结构的指针。在Windows 98中忽略该参数。在Windows NT中,它被设为NULL。

第二个参数是用于新线程的初始堆栈大小,默认值为0。在任何情况下,Windows根据需要动态延长堆栈的大小。

第三个参数是指向线程函数的指标。函数名称没有限制,但是必须以下列形式声明:

DWORD WINAPI ThreadProc (LPVOID pParam) ;

第四个参数为传递给ThreadProc的参数。这样主线程和从属线程就可以共享数据,这个参数既可以是数值,也可以是指向其他信息的指针。

第五个参数通常为0,表示创建之后立刻运行,但当建立的线程不马上执行时为可设置为CREATE_SUSPENDED。线程将暂停直到呼叫ResumeThread来恢复线程的执行为止。

第六个参数时一个返回值,它用来接收线程的ID,可以为该参数传递NULL,表明我们对该线程的ID不感兴趣。

函数返回值为新线程的句柄。

2.CreateEvent函数

HANDLECreateEvent(

LPSECURITY_ATTRIBUTESlpEventAttributes,// 安全属性

BOOLbManualReset,// 复位方式

BOOLbInitialState,// 初始状态

LPCTSTRlpName // 对象名称

);

lpEventAttributes[输入]

一个指向SECURITY_ATTRIBUTES结构的指针,确定返回的句柄是否可被子进程继承。如果lpEventAttributes是NULL,此句柄不能被继承。

Windows NT/2000:lpEventAttributes的结构中的成员为新的事件指定了一个安全符。如果lpEventAttributes是NULL,事件将获得一个默认的安全符。

bManualReset[输入]

指定将事件对象创建成手动复原还是自动复原。如果是TRUE,那么必须用ResetEvent函数来手工将事件的状态复原到无信号状态。如果设置为FALSE,当一个等待线程被释放以后,系统将会自动将事件状态复原为无信号状态。

bInitialState[输入]

指定事件对象的初始状态。如果为TRUE,初始状态为有信号状态;否则为无信号状态。

lpName[输入]

指定事件的对象的名称,是一个以0结束的字符串指针。名称的字符格式限定在MAX_PATH之内。名字是对大小写敏感的。

如果lpName指定的名字,与一个存在的命名的事件对象的名称相同,函数将请求EVENT_ALL_ACCESS来访问存在的对象。这时候,由于bManualReset和bInitialState参数已经在创建事件的进程中设置,这两个参数将被忽略。如果lpEventAttributes是参数不是NULL,它将确定此句柄是否可以被继承,但是其安全描述符成员将被忽略。

如果lpName为NULL,将创建一个无名的事件对象。

如果lpName的和一个存在的信号、互斥、等待计时器、作业或者是文件映射对象名称相同,函数将会失败,在GetLastError函数中将返回ERROR_INVALID_HANDLE。造成这种现象的原因是这些对象共享同一个命名空间。

终端服务(Terminal Services):名称中可以加入"Global\"或是"Local\"的前缀,这样可以明确的将对象创建在全局的或事务的命名空间。名称的其它部分除了反斜杠(\),可以使用任意字符。详细内容可参考Kernel Object Name Spaces。

Windows 2000:在Windows 2000系统中,没有终端服务运行,"Global\"和"Local\"前缀将被忽略。名称的其它部分除了反斜杠(\),可以使用任意字符。

Windows NT 4.0以及早期版本,Windows 95/98:名称中除了反斜杠(\),可以使用任意字符。

// MultiThread.cpp : 定义控制台应用程序的入口点。

代码一:线程不同步的例子(基于事件)

#include "stdafx.h"

#include "stdio.h"

#include "windows.h"

DWORD WINAPI Fun1Proc(LPVOID lpParameter);

DWORD WINAPI Fun2Proc(LPVOID lpParameter);

HANDLE g_hEvent;

int tiket=100;

int _tmain(int argc, _TCHAR* argv[])

{

HANDLE Thread1;

HANDLE Thread2;

g_hEvent=CreateEvent(NULL,FALSE,FALSE,NULL); Thread1=CreateThread(NULL,0,Fun1Proc,NULL,0,NULL);

Thread2=CreateThread(NULL,0,Fun2Proc,NULL,0,NULL);

CloseHandle(Thread1);

CloseHandle(Thread2);

SetEvent(g_hEvent);

//Sleep(10);

system("pause");

return 0;

}

DWORD WINAPI Fun1Proc(LPVOID lpParameter)

{

while(1)

{

//Sleep(1);

if(tiket>0)

{

WaitForSingleObject(g_hEvent,INFINITE);

printf("Thread1 sell tiket %d\n",tiket);

tiket--;

SetEvent(g_hEvent);

}

else

{

//SetEvent(g_hEvent);

break;

}

}

return 0;

}

DWORD WINAPI Fun2Proc(LPVOID lpParameter)

{

while(1)

{

//Sleep(1);

WaitForSingleObject(g_hEvent,INFINITE);

if(tiket>0)

{

printf("Thread2 sell tiket %d\n",tiket);

tiket--;

SetEvent(g_hEvent);

}

else

{

//SetEvent(g_hEvent);

break;

}

}

return 0;

}

代码二:线程同步的例子(基于事件)

#include "stdafx.h"

#include "stdio.h"

#include "windows.h"

DWORD WINAPI Fun1Proc(LPVOID lpParameter);

DWORD WINAPI Fun2Proc(LPVOID lpParameter);

HANDLE g_hEvent;

int tiket=100;

int _tmain(int argc, _TCHAR* argv[])

{

HANDLE Thread1;

HANDLE Thread2;

g_hEvent=CreateEvent(NULL,FALSE,FALSE,NULL); Thread1=CreateThread(NULL,0,Fun1Proc,NULL,0,NULL);

Thread2=CreateThread(NULL,0,Fun2Proc,NULL,0,NULL);

CloseHandle(Thread1);

CloseHandle(Thread2);

SetEvent(g_hEvent);

//Sleep(10);

system("pause");

return 0;

}

DWORD WINAPI Fun1Proc(LPVOID lpParameter)

{

while(1)

{

//Sleep(1);

WaitForSingleObject(g_hEvent,INFINITE);

if(tiket>0)

{

printf("Thread1 sell tiket %d\n",tiket);

tiket--;

SetEvent(g_hEvent);

}

else

{

//SetEvent(g_hEvent);

break;

}

}

return 0;

}

DWORD WINAPI Fun2Proc(LPVOID lpParameter)

{

while(1)

{

//Sleep(1);

WaitForSingleObject(g_hEvent,INFINITE);

if(tiket>0)

{

printf("Thread2 sell tiket %d\n",tiket);

tiket--;

SetEvent(g_hEvent);

}

else

{

//SetEvent(g_hEvent);

break;

}

}

return 0;

}

二:利用互斥对象实现线程同步

CreateMutex()函数可用来创建一个有名或无名的互斥量对象,该对象是一个内核对象,它能够确保线程对单个资源的互斥访问权。

其函数原型为:

HANDLE CreateMutex(

LPSECURITY_ATTRIBUTESlpMutexAttributes, // 指向安全属性的指针

BOOLbInitialOwner, // 初始化互斥对象的所有者

LPCTSTRlpName // 指向互斥对象名的指针

);

返回值:

如执行成功,就返回互斥体对象的句柄;零表示出错。会设置GetLastError。即使返回的是一个有效句柄,但倘若指定的名字已经存在,GetLastError也会设为ERROR_ALREADY_EXISTS。

参数表:

lpMutexAttributes SECURITY_ATTRIBUTES,指定一个SECURITY_ATTRIBUTES结构,

可以赋值NULL,表示让互斥对象拥有默认的安全属性。

bInitialOwner Long:若为TRUE,则创建该互斥对象的线程拥有该互斥对象的所有权,否则,没有所有权。

lpName String,指定互斥体对象的名字。若为NULL,表示创建一个匿名的互斥对象。

注意:如果线程对共享资源的访问结束后,应该释放互斥对象的所有权,即将互斥对象置于有信号的状态,使用函数

BOOL WIANPI ReleaseMutex(

HANDLE hMutex

);

返回值:函数返回成功,返回非0,否则返回0;

参数:互斥对象的句柄。

对于互斥对象遵循谁拥有谁释放的原则。

代码三:

#include "stdafx.h"

#include "stdio.h"

#include "windows.h"

DWORD WINAPI Fun1Proc(LPVOID lpParameter);

DWORD WINAPI Fun2Proc(LPVOID lpParameter);

HANDLE h_Mutex;

int tiket=100;

int _tmain(int argc, _TCHAR* argv[])

{

HANDLE Thread1;

HANDLE Thread2;

h_Mutex=CreateMutex(NULL,FALSE,NULL);

Thread1=CreateThread(NULL,0,Fun1Proc,NULL,0,NULL);

Thread2=CreateThread(NULL,0,Fun2Proc,NULL,0,NULL);

CloseHandle(Thread1);

CloseHandle(Thread2);

//Sleep(10);

system("pause");

return 0;

}

DWORD WINAPI Fun1Proc(LPVOID lpParameter)

{

while(1)

{

//Sleep(1);

WaitForSingleObject(h_Mutex,INFINITE);

if(tiket>0)

{

printf("Thread1 sell tiket %d\n",tiket);

tiket--;

ReleaseMutex(h_Mutex);

}

else

{

break;

}

}

return 0;

}

DWORD WINAPI Fun2Proc(LPVOID lpParameter)

{

while(1)

{

//Sleep(1);

WaitForSingleObject(h_Mutex,INFINITE);

if(tiket>0)

{

printf("Thread2 sell tiket %d\n",tiket);

tiket--;

ReleaseMutex(h_Mutex);

}

else

{

break;

}

}

return 0;

}

三:利用关键代码段(临界区)实现线程同步

关键代码段,也称为临界区,工作在用户方式下,它是指一小段代码,在代码能够执行前,它必须独占对某些资源的访问权。

VOID InitializeCriticalSection(LPCRITICAL_SECTION lpCriticalSection )

函数功能初始化一个临界资源对象。该函数无返回值。单进程的各个线程可以使用临界资源对象来解决同步互斥问题,该对象不能保证哪个线程能够获得到临界资源对象,该系统能公平的对待每一个线程。lpCriticalSection 临界资源对象指针,该参数是一个out类型。 EnterCriticalSection(__inout LPCRITICAL_SECTION lpCriticalSection);

该函数用以获得指定的临界区的对象的所有权,该函数等待临界区对象的所有权,如果该所有权赋予了调用线程,则该函数返回,否则该函数会一直等待,从而导致线程等待。

LeaveCriticalSection(__inout LPCRITICAL_SECTION lpCriticalSection);

线程使用完所保护的资源后,需要调用该函数,释放指定的临界区对象的所有权,这时候其他想要获得该临界区对象所有权的线程就可以获得该所有权,从而进入关键代码段,访问保护的资源。

DeleteCriticalSection(__inout LPCRITICAL_SECTION lpCriticalSection);

该函数释放一个没有被任何线程所拥有的临界区对象的所有资源。

代码四:

#include "stdafx.h"

#include "stdio.h"

#include "windows.h"

DWORD WINAPI Fun1Proc(LPVOID lpParameter);

DWORD WINAPI Fun2Proc(LPVOID lpParameter);

CRITICAL_SECTION g_cs;

int tiket=100;

int _tmain(int argc, _TCHAR* argv[])

{

HANDLE Thread1;

HANDLE Thread2;

Thread1=CreateThread(NULL,0,Fun1Proc,NULL,0,NULL);

Thread2=CreateThread(NULL,0,Fun2Proc,NULL,0,NULL);

CloseHandle(Thread1);

CloseHandle(Thread2);

InitializeCriticalSection(&g_cs);

//Sleep(10);

system("pause");

DeleteCriticalSection(&g_cs);

return 0;

}

DWORD WINAPI Fun1Proc(LPVOID lpParameter)

{

while(1)

{

//Sleep(1);

EnterCriticalSection(&g_cs);

if(tiket>0)

{

printf("Thread1 sell tiket %d\n",tiket);

tiket--;

LeaveCriticalSection(&g_cs);

}

else

{

break;

}

}

return 0;

}

DWORD WINAPI Fun2Proc(LPVOID lpParameter)

{

while(1)

{

//Sleep(1);

EnterCriticalSection(&g_cs);

if(tiket>0)

{

printf("Thread2 sell tiket %d\n",tiket);

tiket--;

LeaveCriticalSection(&g_cs);

}

else

{

break;

}

}

return 0;

}

四:线程死锁

解释:对于多线程来说,如果线程1拥有了临界区对象A,等待临界区对象B的拥有权,线程2拥有了临界区对象B,等待临界区对象A的所有权,这就造成了死锁。

代码五:线程死锁

#include "stdafx.h"

#include "stdio.h"

#include "windows.h"

DWORD WINAPI Fun1Proc(LPVOID lpParameter);

DWORD WINAPI Fun2Proc(LPVOID lpParameter);

CRITICAL_SECTION g_cs;

CRITICAL_SECTION g_cs1;

int tiket=100;

int _tmain(int argc, _TCHAR* argv[])

{

HANDLE Thread1;

HANDLE Thread2;

Thread1=CreateThread(NULL,0,Fun1Proc,NULL,0,NULL);

Thread2=CreateThread(NULL,0,Fun2Proc,NULL,0,NULL);

CloseHandle(Thread1);

CloseHandle(Thread2);

InitializeCriticalSection(&g_cs);

InitializeCriticalSection(&g_cs1);

//Sleep(10);

printf("I am in the main Function!\n");

Sleep(10000);

//system("pause");

DeleteCriticalSection(&g_cs);

DeleteCriticalSection(&g_cs1);

return 0;

}

DWORD WINAPI Fun1Proc(LPVOID lpParameter)

{

while(1)

{

//Sleep(1);

EnterCriticalSection(&g_cs);

Sleep(1);

EnterCriticalSection(&g_cs1);

if(tiket>0)

{

printf("Thread1 sell tiket %d\n",tiket);

tiket--;

LeaveCriticalSection(&g_cs);

LeaveCriticalSection(&g_cs1);

}

else

{

break;

}

}

return 0;

}

DWORD WINAPI Fun2Proc(LPVOID lpParameter)

{

while(1)

{

//Sleep(1);

EnterCriticalSection(&g_cs);

Sleep(1);

EnterCriticalSection(&g_cs1);

if(tiket>0)

{

printf("Thread2 sell tiket %d\n",tiket);

tiket--;

LeaveCriticalSection(&g_cs);

LeaveCriticalSection(&g_cs1);

}

else

{

break;

}

}

return 0;

}

五:总结

互斥对象、事件对象和关键代码段的比较

1.互斥对象和事件对象都属于内核对象,利用内核对象进行线程同步时,速度较慢,但利用互斥对象和事件对象时,可以在多个进程中的多个线程间进行同步。

2.关键代码段工作在用户方式下,同步速度较快,但在使用关键代码段时,很容易进入死锁状态,因为在等待进入关键代码段时无法设置超时值。

时间: 2024-08-04 18:34:20

windows下多线程同步(利用事件对象,互斥对象,关键代码段)实现的相关文章

Delphi多线程编程(10)--多线程同步之Mutex(互斥对象)

原理分析: 互斥对象是系统内核对象,各个线程都可以拥有它,谁拥有它谁就可以执行 执行完毕,用ReleaseMutex 函数释放拥有权,以让其他等待的线程可以使用 其他线程可以使用 WaitForSingleObject函数排队等待(等待也可以理解为排队申请) 使用过程 var hMutex: THandle; {应该先声明一个全局的互斥句柄} CreateMutex {建立互斥对象} WaitForSingleObject {用等待函数排队等候} ReleaseMutex {释放拥有权} Clo

windows平台多线程同步实现之Mutex对象的使用

windows平台多线程同步实现之MutexMutex对象的使用 前言 线程组成: 线程的内核对象,操作系统用来管理该线程的数据结构. 线程堆栈,它用于维护线程在执行代码时需要的所有参数和局部变量. ??操作系统为每一个运行线程安排一定的CPU时间 -- 时间片.系统通过一种循环的方式为线程提供时间片,线程在自己的时间内运行,多个线程不断地切换运行,因时间片相当短,因此,给用户的感觉,就好像线程是同时运行的一样. ??单cpu计算机一个时间只能运行一个线程,如果计算机拥有多个CPU,线程就能真正

Windows 下rsync同步数据报错7456

Windows下rsync同步数据报错如下图: 解决方法:此报错原因(rsync服务器端无法连接到客户端212.246)检测69.251服务器端到客户端的rsync端口是否通 873 发现69.251到客户端212.246的端口873是不通的,在212.246上面的防火墙规则加上873端口.再次运行此同步脚本即可.

VC++深入详解——第16章:线程同步,关键代码段

关键代码段,也称为临界区,工作在用户方式下,它是指一小段代码,在代码能够执行前,它必须独占对某些资源的访问权. CRITICAL_SECTION型结构体. 关键代码段的相关函数: InitializeCriticalSection函数: 进入关键代码段前需要进行初始化. EnterCriticalSection函数: 获得临界区对象的所有权,如果所有权赋予了调用线程,那么就返回,否则会一直等待. LeaveCriticalSection函数: 释放指定的临界区的所有权. DeleteCritic

Windows下多线程编程(一)

前言 熟练掌握Windows下的多线程编程,能够让我们编写出更规范多线程代码,避免不要的异常.Windows下的多线程编程非常复杂,但是了解一些常用的特性,已经能够满足我们普通多线程对性能及其他要求. 进程与线程 1. 进程的概念 进程就是正在运行的程序.主要包括两部分: • 一个是操作系统用来管理进程的内核对象.内核对象也是系统用来存放关于进程的统计信息的地方. • 另一个是地址空间,它包含所有可执行模块或 D L L模块的代码和数据.它还包含动态内 2. 线程的概念 线程就是描述进程的一条执

Windows下多线程编程(二)

线程的分类 1.     有消息循环线程 MFC中有用户界面线程,从CWinThread派生出一个新的类作为UI线程类CUIThread,然后调用AfxBeginthread(RUNTIME_CLASS(CUIThread));启动线程.UI线程可以直接创建模态对话框,而不用担心消息循环的问题,因为UI线程默认自带消息循环. MFC非用户界面线程,不能创建模态对话框,但是可以创建非模态对话框或普通窗口,但是必须自己写消息循环. MSG msg; while(GetMessage(&msg, NU

Delphi多线程编程(12)--多线程同步之Semaphore(信号对象)

转载自:万一的博客 之前已经有了两种线程同步的方法: CriticalSection(临界区)和Mutex(互斥)吗,这两种同步方法差不多,只是作用域不同 CriticalSection类似于只有一个蹲位的公共厕所,只能一个个地进 Mutex 对象类似于接力赛中的接力棒,某一时刻只能有一个人持有,谁拿着谁跑 什么是Semaphore(信号或叫信号量)呢? 譬如到银行办业务.或者到车站买票,原来只有一个服务员,不管有多少人排队等候,业务只能一个个地来 假如增加业务窗口,可以同时受理几个业务呢? 这

Windows下多线程编程

熟练掌握Windows下的多线程编程,能够让我们编写出更规范多线程代码,避免不要的异常.Windows下的多线程编程非常复杂,但是了解一些常用的特性,已经能够满足我们普通多线程对性能及其他要求. 进程与线程 1. 进程的概念 进程就是正在运行的程序.主要包括两部分: ? 一个是操作系统用来管理 ... bbs.chinaacc.com/forum-2-3/topic-5662298.html bbs.chinaacc.com/forum-2-3/topic-5662294.html bbs.ch

Windows下自动同步文件的小脚本

分享两个自己整理的在windows下自动定时同步文件的bat小脚本 (照猫画虎 o(∩_∩)o ) ******************************************************************* @echo off color 02 rem 使用goto和lftp来实现定时自动上传文件到ftp服务器指定目录         ##注释信息 :loop echo.   echo.   echo =================================