windows线程同步的总结
一 线程
1)如果你正在编写C/C++代码,决不应该调用CreateThread。相反,应该使用VisualC++运行期库函数_beginthreadex,退出也应该使用_endthreadex。如果不使用Microsoft的VisualC++编译器,你的编译器供应商有它自己的CreateThred替代函数。不管这个替代函数是什么,你都必须使用。
2)因为_beginthreadex和_endthreadex是CRT线程函数,所以必须注意编译选项runtimelibaray的选择,使用MT或MTD。
3) _beginthreadex函数的参数列表与CreateThread函数的参数列表是相同的,但是参数名和类型并不完全相同。这是因为Microsoft的C/C++运行期库的开发小组认为,C/C++运行期函数不应该对Windows数据类型有任何依赖。_beginthreadex函数也像CreateThread那样,返回新创建的线程的句柄。
4)下面是关于_beginthreadex的一些要点:
•每个线程均获得由C/C++运行期库的堆栈分配的自己的tiddata内存结构。(tiddata结构位于Mtdll.h文件中的VisualC++源代码中)。
•传递给_beginthreadex的线程函数的地址保存在tiddata内存块中。传递给该函数的参数也保存在该数据块中。
•_beginthreadex确实从内部调用CreateThread,因为这是操作系统了解如何创建新线程的唯一方法。
•当调用CreatetThread时,它被告知通过调用_threadstartex而不是pfnStartAddr来启动执行新线程。还有,传递给线程函数的参数是tiddata结构而不是pvParam的地址。
•如果一切顺利,就会像CreateThread那样返回线程句柄。如果任何操作失败了,便返回NULL。
5) _endthreadex的一些要点:
•C运行期库的_getptd函数内部调用操作系统的TlsGetValue函数,该函数负责检索调用线程的tiddata内存块的地址。
•然后该数据块被释放,而操作系统的ExitThread函数被调用,以便真正撤消该线程。当然,退出代码要正确地设置和传递。
6) 虽然也提供了简化版的的_beginthread和_endthread,但是可控制性太差,所以一般不使用。
6)线程handle因为是内核对象,所以需要在最后close handle。
7)C++主线程的终止,同时也会终止所有主线程创建的子线程,不管子线程有没有执行完毕。
8)如果某线程挂起,然后有调用WaitForSingleObject等待该线程,就会导致死锁。
二 线程同步之Critical Sections
1) 因为Critical Sections不是内核对象,所以只能用来同一进程内线程间的同步,不能用来多个不同进程间的线程的同步。
2) 如果在Critical Sections中间突然程序crash或是exit而没有调用LeaveCriticalSection,则结果是该线程所对应的内核不能被释放,该线程成为死线程。
3) 要比其他的内核对象的速度要快。
三 线程同步之Mutex
1)互斥对象(mutex)内核对象能够确保线程拥有对单个资源的互斥访问权。实际上互斥对象是因此而得名的。互斥对象包含一个使用数量,一个线程ID和一个递归计数器。
2) 互斥对象的行为特性与关键代码段相同,但是互斥对象属于内核对象,而关键代码段则属于用户方式对象。这意味着互斥对象的运行速度比关键代码段要慢。但是这也意味着不同进程中的多个线程能够访问单个互斥对象,并且这意味着线程在等待访问资源时可以设定一个超时值。
3) ID用于标识系统中的哪个线程当前拥有互斥对象,递归计数器用于指明该线程拥有互斥对象的次数。
4) 互斥对象有许多用途,属于最常用的内核对象之一。通常来说,它们用于保护由多个线程访问的内存块。如果多个线程要同时访问内存块,内存块中的数据就可能遭到破坏。互斥对象能够保证访问内存块的任何线程拥有对该内存块的独占访问权,这样就能够保证数据的完整性。
5)互斥对象的使用规则如下:
• 如果线程ID是0(这是个无效ID),互斥对象不被任何线程所拥有,并且发出该互斥对象的通知信号。
• 如果ID是个非0数字,那么一个线程就拥有互斥对象,并且不发出该互斥对象的通知信号。
• 与所有其他内核对象不同, 互斥对象在操作系统中拥有特殊的代码,允许它们违反正常的规则。
四 线程同步之Event
1)在所有的内核对象中,事件内核对象是个最基本的对象。它们包含一个使用计数(与所有内核对象一样),一个用于指明该事件是个自动重置的事件还是一个人工重置的事件的布尔值,另一个用于指明该事件处于已通知状态还是未通知状态的布尔值。
2)事件能够通知一个操作已经完成。有两种不同类型的事件对象。一种是人工重置的事件,另一种是自动重置的事件。当人工重置的事件得到通知时,等待该事件的所有线程均变为可调度线程。当一个自动重置的事件得到通知时,等待该事件的线程中只有一个线程变为可调度线程。
3)当一个线程执行初始化操作,然后通知另一个线程执行剩余的操作时,事件使用得最多。事件初始化为未通知状态,然后,当该线程完成它的初始化操作后,它就将事件设置为已通知状态。这时,一直在等待该事件的另一个线程发现该事件已经得到通知,因此它就变成可调度线程。
4)Microsoft为自动重置的事件定义了应该成功等待的副作用规则,即当线程成功地等待到该对象时,自动重置的事件就会自动重置到未通知状态。这就是自动重置的事件如何获得它们的名字的方法。通常没有必要为自动重置的事件调用ResetEvent函数,因为系统会自动对事件进行重置。但是,Microsoft没有为人工重置的事件定义成功等待的副作用,所以需要调用ResetEvent()。
五 线程同步之信号量(Semaphore)
信号量(Semaphore)内核对象对线程的同步方式与前面几种方法不同,它允许多个线程在同一时刻访问同一资源,但是需要限制在同一时刻访问此资源的最大线程数目。在用 CreateSemaphore()创建信号量时即要同时指出允许的最大资源计数和当前可用资源计数。一般是将当前可用资源计数设置为最大资源计数,每增加一个线程对共享资源的访问,当前可用资源计数就会减1,只要当前可用资源计数是大于0的,就可以发出信号量信号。但是当前可用计数减小到0时则说明当前占用资源的线程数已经达到了所允许的最大数目,不能在允许其他线程的进入,此时的信号量信号将无法发出。线程在处理完共享资源后,应在离开的同时通过 ReleaseSemaphore()函数将当前可用资源计数加1。在任何时候当前可用资源计数决不可能大于最大资源计数。
使用信号量内核对象进行线程同步主要会用到CreateSemaphore()、OpenSemaphore()、ReleaseSemaphore()、 WaitForSingleObject()和WaitForMultipleObjects()等函数。
六 线程同步之其他
1)线程局部存储 (TLS),同一进程中的所有线程共享相同的虚拟地址空间。不同的线程中的局部变量有不同的副本,但是static和globl变量是同一进程中的所有线程共享的。使用TLS技术可以为static和globl的变量,根据当前进程的线程数量创建一个array,每个线程可以通过array的index来访问对应的变量,这样也就保证了static和global的变量为每一个线程都创建不同的副本。
2)互锁函数的家族十分的庞大,例如InterlockedExchangeAdd()。。。,使用互锁函数的优点是:他的速度要比其他的CriticalSection,Mutex,Event,Semaphore快很多。
3)等待函数,例如WaitForSingleObject 函数用来检测 hHandle 事件的信号状态,当函数的执行时间超过 dwMilliseconds 就返回,但如果参数 dwMilliseconds 为 INFINITE 时函数将直到相应时间事件变成有信号状态才返回,否则就一直等待下去,直到 WaitForSingleObject 有返回直才执行后面的代码。
以下是写的代码
CThread.h
#include <process.h>
#include <Windows.h> //#define CRITICALSECTION #define MUTEX
//#define SEMAPHORE
#define EVENT #ifdef CRITICALSECTION class CriticalSection
{
public:
CriticalSection(){ InitializeCriticalSection(&criticalS); }
~CriticalSection(){ DeleteCriticalSection(&criticalS); }
void enterCriticalSection(){ EnterCriticalSection(&criticalS); }
void leaveCriticalSection(){ LeaveCriticalSection(&criticalS);}
private:
CRITICAL_SECTION criticalS;
}; class CLock
{
public:
CLock(CriticalSection *crs):cs(crs){lock();}
~CLock(){unlock();}
void lock(){ cs->enterCriticalSection() ;}
void unlock(){ cs->leaveCriticalSection(); } private:
CriticalSection *cs;
}; #endif
CThread.cpp
// CThread.cpp : Defines the entry point for the console application.
// #include "stdafx.h"
#include "CThread.h"
//#include <Windows.h> #define MAX1 1
#define MAX2 10 #define INITRESOURCE 10
#define MAXRESOURCE 10 #define THREADNUM 5 #define Thread __declspec(thread) Thread static int g_tls = 0; int g_count = 0; #ifdef CRITICALSECTION
CriticalSection critSct;
#endif #ifdef MUTEX
HANDLE g_mutex;
#endif #ifdef SEMAPHORE
HANDLE g_semaphore;
#endif #ifdef EVENT
HANDLE g_event;
#endif unsigned _stdcall thread1Entry(void *pList)
{
#ifdef CRITICALSECTION
CLock myLock(&critSct);
#endif #ifdef SEMAPHORE
WaitForSingleObject(g_semaphore, INFINITE);
#endif #ifdef MUTEX
WaitForSingleObject(g_mutex, INFINITE);
#endif #ifdef EVENT
WaitForSingleObject(g_event, INFINITE);
#endif for( int i = 0; i < MAX1; i++)
{
g_count++;
printf("threa1:%d\n",g_count);
}
#ifdef EVENT
SetEvent(g_event);
#endif
//if( g_count == 100 )
//{
//long preciousCount = 0;
//ReleaseSemaphore(g_semaphore, 2, &preciousCount);
//printf("thread precious count is:%d\n", preciousCount);
//}
return 0; } unsigned _stdcall thread2Entry(void *pList)
{
#ifdef CRITICALSECTION
CLock myLock(&critSct);
#endif #ifdef MUTEX
WaitForSingleObject(g_mutex, INFINITE);
#endif #ifdef EVENT
WaitForSingleObject(g_event, INFINITE);
#endif for( int i = MAX1; i < MAX2; i++)
{
g_count++;
printf("threa2:%d\n",g_count);
} #ifdef EVENT
SetEvent(g_event);
#endif return 0;
} void loadDataIntoMemory()
{
printf("load data into memory!\n");
} int _tmain(int argc, _TCHAR* argv[])
{
#ifdef MUTEX
g_mutex = CreateMutex(NULL, false, NULL);
if( g_mutex == NULL )
{
printf("create mutex failed with error %d\n", GetLastError());
return 1;
}
#endif #ifdef SEMAPHORE
g_semaphore = CreateSemaphore(NULL, INITRESOURCE, MAXRESOURCE, NULL);
#endif #ifdef EVENT
g_event = CreateEvent(NULL, false, false, NULL);
#endif HANDLE thread[THREADNUM + 1]; for( int i = 0; i < THREADNUM; i++ )
{
thread[i] = (HANDLE)_beginthreadex(NULL, 0, thread1Entry, NULL, 0, NULL);
if( thread[i] == NULL )
{
printf("create thread failed with error %d\n", GetLastError());
return 1;
}
} #ifdef EVENT
thread[5] = (HANDLE)_beginthreadex(NULL, 0, thread2Entry, NULL, 0, NULL); loadDataIntoMemory(); SetEvent(g_event);
#endif WaitForMultipleObjects(THREADNUM, thread, true, INFINITE); for( int i = 0; i < THREADNUM; i++ )
{
CloseHandle(thread[i]);
} #ifdef MUTEX
ReleaseMutex(g_mutex);
#endif #ifdef SEMAPHORE
long preciousCount = 0;
ReleaseSemaphore(g_semaphore, 0, &preciousCount);
printf("precious count is:%d\n", preciousCount);
#endif return 0;
}
windows线程同步的总结的更多相关文章
- windows线程同步
一.前言 之前在项目中,由于需要使用到多线程,多线程能够提高执行的效率,同时也带来线程同步的问题,故特此总结如下. 二.windows线程同步机制 windows线程同步机制常用的有几种:Event. ...
- 关于windows线程同步的四种方法
#include "stdafx.h" #include "iostream" #include "list" #include " ...
- Windows线程同步(下)
线程同步三:事件 CreateEvent:Creates or opens a named or unnamed event object. HANDLE WINAPI CreateEvent( _I ...
- Windows线程同步(上)
先介绍一个创建线程的API,参考:https://msdn.microsoft.com/en-us/library/windows/desktop/ms682453%28v=vs.85%29.aspx ...
- windows线程同步的几种方式
以下为main函数的测试代码 具体线程同步的实现代码请下载:https://github.com/kingsunc/ThreadSynchro #include <stdio.h> #in ...
- windows 线程同步
Windows 临界区,内核事件,互斥量,信号量. 临界区,内核事件,互斥量,信号量,都能完成线程的同步,在这里把他们各自的函数调用,结构定义,以及适用情况做一个总结. 临界区: 适用范围:它只能同步 ...
- 总结windows多线程同步互斥
windows多线程同步互斥--总结 我的windows多线程系列文章: windows多线程--原子操作 windows多线程同步--事件 windows多线程同步--互斥量 windows多线程同 ...
- Windows线程+进程通信
一 Windows线程进程 1)定义 按照MS的定义, Windows中的进程简单地说就是一个内存中的可执行程序, 提供程序运行的各种资源. 进程拥有虚拟的地址空间, 可执行代码, 数据, 对象句柄集 ...
- 操作系统中的进程同步与Window中利用内核对象进行线程同步的关系
操作系统中为了解决进程间同步问题提出了用信号量机制,信号量可分为四种类型分别是互斥型信号量,记录型信号量,AND型信号量,信号量集. 互斥型信号量 互斥型信号量是资源数量为1的特殊的记录型信号量.表示 ...
随机推荐
- Cloud Engine
Cloud Engine:大杀器如何炼成 郑昀 创建于2016/6/18 最后更新于2016/6/19 点击查看我的<如何从零搭建一个技术平台>,这是一个系列.转载时请注明“转载自旁观 ...
- C#写PDF文件类库PDF File Writer介绍
.NET平台开源项目速览(16)C#写PDF文件类库PDF File Writer介绍 阅读目录 1.PDF File Writer基本介绍 2.一个简单的使用案例 3.资源 1年前,我在文章:这 ...
- Android项目实战手机安全卫士(02)
目录 项目结构图 源代码 运行结果 项目源代码 项目结构图 源代码 清单 01. SplashActivity.java package com.coderdream.mobilesafe.acti ...
- 基于visual Studio2013解决算法导论之003雇佣问题
题目 雇用问题 解决代码及点评 #include <stdio.h> #include <stdlib.h> #include <malloc.h> #in ...
- BZOJ 2705: [SDOI2012]Longge的问题( 数论 )
T了一版....是因为我找质因数的姿势不对... 考虑n的每个因数对答案的贡献. 答案就是 ∑ d * phi(n / d) (d | n) 直接枚举n的因数然后求phi就行了. 但是我们可以做的更好 ...
- 使用ssh远程执行命令批量导出数据库到本地(转)
前天正在跟前端的同事调试功能.服务器开好,模拟的玩家登录好,就在倒计时.这时突然运营的同事跑过来说要统计几个服务器玩家的一些情况,也就是需要从几个服的数据库导出部分玩家的数据.好吧,我看了一下时间,1 ...
- getAttribute()获取属性
Js:getAttribute[转] 一份文档就是一棵节点树. ●节点分为不同的类型:元素节点.属性节点和文本节点等. ●getElementById()方法将返回一个对象,该对象对应着文档里的一个特 ...
- UVA 10574 - Counting Rectangles(枚举+计数)
10574 - Counting Rectangles 题目链接 题意:给定一些点,求可以成几个边平行于坐标轴的矩形 思路:先把点按x排序,再按y排序.然后用O(n^2)的方法找出每条垂直x轴的边,保 ...
- 网页内容的html标签补全和过滤的两种方法
网页内容的html标签补全和过滤的两种方法: 假设你的网页内容的html标签显示不全,有些表格标签不完整而导致页面混乱,或者把你的内容之外的局部html页面给包括进去了,我们能够写个函数方法来补全ht ...
- 【linux】开发环境说明
欢迎转载,转载时请保留作者信息,谢谢. 邮箱:tangzhongp@163.com 博客园地址:http://www.cnblogs.com/embedded-tzp Csdn博客地址:http:// ...