- 浏览: 2018173 次
- 性别:
- 来自: 北京
文章分类
- 全部博客 (795)
- java (263)
- 聚类搜索引擎 (9)
- 经验之谈 (67)
- DSP (3)
- C++ (140)
- Linux (37)
- SNMP (6)
- Python (6)
- 数据库 (61)
- 网络 (20)
- 算法 (15)
- 设计模式 (4)
- 笔试题 (38)
- 散文 (35)
- 数据结构 (9)
- 银行知识 (0)
- 榜样 (9)
- Lucene (15)
- Heritrix (6)
- MetaSeeker (0)
- netbeans (12)
- php (3)
- 英语 (8)
- DB2 (0)
- java基础 (5)
- mongodb & hadoop (4)
- Javascript (7)
- Spring (4)
- ibatis & myibatis (1)
- velocity (1)
- 微服务 (0)
- paddle (1)
- 第三方 (0)
- 知识沉淀 (1)
- 建模 (0)
最新评论
-
0372:
标示对java很陌生!
中文乱码解决的4种方式 -
梦留心痕:
Java中\是转意字符, 可是你的这句话我没看懂,只要把得到的 ...
java中如何忽略字符串中的转义字符--转载 -
yanjianpengit:
[b][/b]
java为什么非静态内部类里面不能有静态成员 -
springdata-jpa:
可以参考最新的文档:如何在eclipse jee中检出项目并转 ...
eclipse 如何把java项目转成web项目 -
qq1130127172:
,非常好。
(转)SpringMVC 基于注解的Controller @RequestMapping @RequestParam..
线程同步是多线程程序设计的核心内容,它的目的是正确处理多线程并发时的各种问题,例如线程的等待、多个线程访问同一数据时的互斥,防死锁等。Win32提供多种内核对象和手段用于线程同步,如互斥量、信号量、事件、临界区等。所不同的是,互斥量、信号量、事件都是Windows的内核对象,当程序对这些对象进行控制时会自动转换到核心态,而临界区本身不是内核对象,它是工作在用户态的。我们知道从用户态转换到核心态是需要以时间为代价的,所以如果能在用户态就简单解决的问题,就可以不必劳烦核心态了。
这里我要说的是两种用于C++的多线程同步类,通过对这两种类的使用就可以方便的实现对变量或代码段的加锁控制,从而防止多线程对变量不正确的操作。
所谓加锁,就是说当我们要访问某关键变量之前,都需要首先获得允许才能继续,如果未获得允许则只有等待。一个关键变量拥有一把锁,一个线程必须先得到这把锁(其实称为钥匙可能更形象)才可以访问这个变量,而当某个变量持有这把锁的时候,其他线程就不能重复的得到它,只有等持有锁的线程把锁归还以后其他线程才有可能得到它。之所以这样做,就是为了防止一个线程读取某对象途中另一线程对它进行了修改,或两线程同时对一变量进行修改,例如:
// 全局:
struct MyStruct ... { int a, b; } ;
MyStruct s;
// 线程1:
int a = s.a;
int b = s.b;
// 线程2:
s.a ++ ; s.b -- ;
如果实际的执行顺序就是上述书写的顺序那到没有什么,但如果线程2的执行打断了线程1,变为如下顺序:
int a = s.a; //线程1
s.a++; //线程2
s.b++; //线程2
int b = s.b; //线程1
那么这时线程1读出来的a和b就会有问题了,因为a是在修改前读的,而b是在修改后读的,这样读出来的是不完整的数据,会对程序带来不可预料的后果。天知道两个程的调度顺序是什么样的。为了防止这种情况的出现,需要对变量s加锁,也就是当线程1得到锁以后就可以放心的访问s,这时如果线程2要修改s,只有等线程1访问完成以后将锁释放才可以,从而保证了上述两线程交叉访问变量的情况不会出现。
使用Win32提供的临界区可以方便的实现这种锁:
// 全局:
CRITICAL_SECTION cs;
InitializeCriticalSection( & cs);
// 线程1:
EnterCriticalSection( & cs);
int a = s.a;
int b = s.b;
LeaveCriticalSection( & cs);
// 线程2:
EnterCriticalSection( & cs);
s.a ++ ;
s.b -- ;
LeaveCriticalSection( & cs);
// 最后:
DeleteCriticalSection( & cs);
代码中的临界区变量(cs)就可以看作是变量s的锁,当函数EnterCriticalSection返回时,当前线程就获得了这把锁,之后就是对变量的访问了。访问完成后,调用LeaveCriticalSection表示释放这把锁,允许其他线程继续使用它。
如果每当需要对一个变量进行加锁时都需要做这些操作,显得有些麻烦,而且变量cs与s只有逻辑上的锁关系,在语法上没有什么联系,这对于锁的管理带来了不小的麻烦。程序员总是最懒的,可以想出各种偷懒的办法来解决问题,例如让被锁的变量与加锁的变量形成物理上的联系,使得锁变量成为被锁变量不可分割的一部分,这听起来是个好主意。
首先想到的是把锁封闭在一个类里,让类的构造函数和析构函数来管理对锁的初始化和锁毁动作,我们称这个锁为“实例锁”:
class InstanceLockBase
... {
CRITICAL_SECTION cs;
protected :
InstanceLockBase() ... { InitialCriticalSection( & cs); }
~ InstanceLockBase() ... { DeleteCriticalSection( & cs); }
} ;
如果熟悉C++,看到这里一定知道后面我要干什么了,对了,就是继承,因为我把构造函数和析构函数都声明为保护的(protected),这样唯一的作用就是在子类里使用它。让我们的被保护数据从这个类继承,那么它们不就不可分割了吗:
struct MyStruct: public InstanceLockBase... { … } ;
什么?结构体还能从类继承?当然,C++中结构体和类除了成员的默认访问控制不同外没有什么不一样,class能做的struct也能做。此外,也许你还会问,如果被锁的是个简单类型,不能继承怎么办,那么要么用一个类对这个简单类型进行封装(记得Java里有int和Integer吗),要么只好手工管理它们的联系了。如果被锁类已经有了基类呢?没关系,C++是允许多继承的,多一个基类也没什么。
现在我们的数据里面已经包含一把锁了,之后就是要添加加锁和解锁的动作,把它们作为InstanceLockBase类的成员函数再合适不过了:
class InstanceLockBase
... {
CRITICAL_SECTION cs;
void Lock() ... { EnterCriticalSection( & cs); }
void Unlock() ... { LeaveCriticalSection( & cs); }
…
} ;
看到这里可能会发现,我把Lock和Unlock函数都声明为私有了,那么如何访问这两个函数呢?是的,我们总是需要有一个地方来调用这两个函数以实现加锁和解锁的,而且它们总应该成对出现,但C++语法本身没能限制我们必须成对的调用两个函数,如果加完锁忘了解,那后果是严重的。这里有一个例外,就是C++对于构造函数和析构函数的调用是自动成对的,对了,那就把对Lock和Unlock的调用专门写在一个类的构造函数和析构函数中:
class InstanceLock
... {
InstanceLockBase * _pObj;
public :
InstanceLock(InstanceLockBase * pObj)
... {
_pObj = pObj; // 这里会保存一份指向s的指针,用于解锁
if (NULL != _pObj)
_pObj -> Lock(); // 这里加锁
}
~ InstanceLock()
... {
if (NULL != _pObj)
_pObj -> Unlock(); // 这里解锁
} } ;
最后别忘了在类InstanceLockBase中把InstanceLock声明为友元,使得它能正确访问Lock和Unlock这两个私有函数:
class InstanceLockBase
... {
friend class InstanceLock;
…
} ;
好了,有了上面的基础,现在对变量s的加解锁管理变成了对InstanceLock的实例的生命周期的管理了。假如我们有一个函数ModifyS中要对s进行修改,那么只要在函数一开始就声明一个InstaceLock的实例,这样整个函数就自动对s加锁,一旦进入这个函数,其他线程就都不能获得s的锁了:
void ModifyS()
... {
InstanceLock lock ( & s); // 这里已经实现加锁了
// some operations on s
} // 一旦离开lock
如果是要对某函数中一部分代码加锁,只要用一对大括号把它们括起来再声明一个lock就可以了:
... {
InstanceLock lock ( & s);
// do something …
}
好了,就是这么简单。下面来看一个测试。 首先准备一个输出函数,对我们理解程序有帮助。它会在输出我们想输出的内容同时打出行号和时间:
void Say( char * text)
... {
static int count = 0 ;
SYSTEMTIME st;
::GetLocalTime( & st);
printf( " %03d [%02d:%02d:%02d.%03d]%s " , ++ count, st.wHour, st.wMinute, st.wSecond, st.wMilliseconds, text);
}
当然,原则上当多线程都调用这个函数时应该对其静态局部变量count进行加锁,这里就省略了。 我们声明一个非常简单的被锁的类型,并生成一个实例:
class MyClass: public InstanceLockBase
... {} ;
MyClass mc;
子线程的任务就是对这个对象加锁,然后输出一些信息:
DWORD CALLBACK ThreadProc(LPVOID param)
... {
InstanceLock il( & mc);
Say( " in sub thread, lock " );
Sleep( 2000 );
Say( " in sub thread, unlock " );
return 0 ;
}
这里会输出两条信息,一是在刚刚获得锁的时间,二是在释放锁的时候,中间通过Sleep来延迟2秒。
主线程负责开启子线程,然后也对mc加锁:
CreateThread( 0 , 0 , ThreadProc, 0 , 0 , 0 );
... {
InstanceLock il( & mc);
Say( " in main thread, lock " );
Sleep( 3000 );
Say( " in main thread, lock " );
}
运行此程序,得到的输出如下:
001 [13:43:23.781]in main thread, lock
002 [13:43:26.781]in main thread, lock
003 [13:43:26.781]in sub thread, lock
004 [13:43:28.781]in sub thread, unlock
从其输出的行号和时间可以清楚的看到两个线程间的互斥:当主线程恰好首先获得锁时,它会延迟3秒,然后释放锁,之后子线程才得以继续进行。这个例子也证明我们的类工作的很好。
总结一下,要使用InstanceLock系列类,要做的就是:
1、让被锁类从InstanceLockBase继承
2、所有要访问被锁对象的代码前面声明InstanceLock的实例,并传入被锁对象的指针。
附:完整源代码:
#pragma once
#include < windows.h >
class InstanceLock;
class InstanceLockBase
... {
friend class InstanceLock;
CRITICAL_SECTION cs;
void Lock()
... { ::EnterCriticalSection( & cs); }
void Unlock()
... {::LeaveCriticalSection( & cs); }
protected : InstanceLockBase()
... { ::InitializeCriticalSection( & cs);
}
~ InstanceLockBase()
... {
::DeleteCriticalSection( & cs);
} } ;
class InstanceLock
... { InstanceLockBase * _pObj;
public :
InstanceLock(InstanceLockBase * pObj)
... {
_pObj = pObj;
if (NULL != _pObj)
_pObj -> Lock();
}
~ InstanceLock()
... {
if (NULL != _pObj)
_pObj -> Unlock();
} } ;
发表评论
-
DLL中导出函数的声明有两种方式:
2012-11-12 16:42 1815DLL中导出函数的声明有两种方式: 一种方式是:在函数声明中 ... -
k-means算法的C++实现
2011-04-05 11:38 2304k-means算法的C++实现: http://www.ku ... -
main()中的参数
2010-10-31 10:41 1506所有的应用程序都是从以main函数作为入口, 而mai ... -
static作用
2010-10-26 19:15 2346转自(from http://www.cnb ... -
mmap函数
2010-10-25 22:41 1888mmap函数的使用方法 UNIX ... -
C语言中三种内存分配方式
2010-10-25 20:23 01.malloc 原型:extern void *ma ... -
位拷贝和值拷贝
2010-10-23 15:37 1567为了便于说明我们以String类为例: 首先定义String ... -
(转帖)把类的析构函数写成虚函数的用意
2010-10-23 15:10 1670#include <iostream.h> cl ... -
动态规划/贪心算法----0/1背包问题AND普通背包问题
2010-10-23 14:03 6797两个背包问题都是比较典型的问题,对这两种算法的理解有很好的帮助 ... -
netstat, nslookup, finger, ping命令
2010-10-22 17:13 1492Netstat用于显示与IP、TCP ... -
C++返回值
2010-10-22 16:53 1523C++函数返回值: (1)正常情况下,函数的参数要复制一份在 ... -
switch语句后的表达式的值
2010-10-22 16:23 1815一般格式: switch (表达式) { case 常量 ... -
C++四种强制类型转换
2010-10-19 11:45 1538显式类型转换又被称之 ... -
C++四种强制类型转化的区别
2010-10-19 11:43 1328先介绍const_cast和reinterpret_cast: ... -
Visual C++线程同步技术剖析:临界区,时间,信号量,互斥量
2010-10-18 14:24 1803使线程同步 在程序中使用多线程时,一般很少有多个线程能在其 ... -
(转)临界区,互斥量,信号量,事件的区别
2010-10-18 14:22 1744四种进程或线程同步互斥的控制方法1、临界区:通过对多线程的串行 ... -
(转)在C++中实现同步锁,类似synchronize(object){....}
2010-10-18 13:49 1857在做C++的项目中发现, ... -
C++多线程编程
2010-10-18 10:56 1726今天我给大家讲一讲C++ ... -
关于C++对函数传参与函数返回值进行引用传递的详解
2010-10-16 22:51 4028关于C++对函数传参与函数返回值进行引用传递的详解 ... -
i++和i--哪个效率高
2010-10-15 09:01 3255for(i = n; i > 0; i--){…} ...
相关推荐
visual studio C++线程同步
Visual_C++线程同步技术剖析[汇编].pdf
本文实例分析了C++线程同步问题,分享给大家供大家参考。具体分析如下: 该实例设置全局变量g_bContinue,在主线程中设置全局变量g_bContinue,工作线程检测该全局变量,实现主线程控制工作线程的目的。 打印出的g_...
多线程同步技术是计算机软件开发的重要技术,本文对多线程的各种同步技术的原理和实现进行了初步探讨。 讲解了windows内核编程中个中同步方法的原理,并有代码示例 吐血推荐
对线程间通信的事件和信号量进行的功能封装,简单易用
很不错的源码,3种多线程实现同步方法
本工程中包含了线程同步的五种方法,现在拿出来和大家一起分享,VC6.0编译测试通过,工程中包含了5个小工程,具体讲述每种线程同步方法的具体使用列子,而且有详细的注释。
讲述了c++开发中多线程的开发技巧。里面有代码实例
c++多线程同步——信号量。非常简单的MFC工程。
在windows平台上建立的c++多线程demo,利用信号量实现线程同步功能。
主要介绍了C++ 线程(串行 并行 同步 异步)详解的相关资料,需要的朋友可以参考下
利用多线程原理模拟生产与消费的互斥同步过程,使用了信号量
VisualC++线程同步技术剖析临界区,时间,信号量,互斥量[定义].pdf
本文实例讲述了C++使用CriticalSection实现线程同步的方法,在前文C++线程同步实例分析的基础上增加了四行代码,使用了四个函数: EnterCriticalSection ::DeleteCriticalSection ::EnterCriticalSection ::...
大家不要下载,这程序是本人上传的,只是实现了多线程和互斥,老师说不能算是同步。
这是一个Visual C++临界区域线程同步的简单实例工程。
本人用c++实现的win32线程同步方案归纳,简洁明了,只用做给新手的基本演示,创建线程所用函数为微软c运行时库的_beginthreadex,是微软最推荐使用的函数。 总共5种线程同步方案:事件、信号量、全局变量、临界区、...
激光雷达数据读取以及显示C++