【分享】在C++里实现 Java 的 synchronized 线程同步关键字

zjcqoo 2013-01-13 12:13:52
  话说Java里有个很强大的关键字叫synchronized,可以方便的实现线程同步。我们尝试在C++里模拟一个类似的。
  Java里的synchronized有两种形式,一种是基于函数的,另种则是语块的。前者受C++的语法所限,估计是没法实现了,所以就尝试后者。
  块级语法很简单:
synchronized(syncObject) {
// code
}

  因为Java所有变量都继承于Object,所以任意变量都能当作锁用。这在C++里无法简易实现,因此我们用特定的类型实例当作同步变量使用。
  先从最经典简易的同步类说起。
struct Lock : CRITICAL_SECTION {
Lock() {
::InitializeCriticalSection(this);
}

~Lock() {
::DeleteCriticalSection(this);
}

void Enter() {
::EnterCriticalSection(this);
}

void Leave() {
::LeaveCriticalSection(this);
}
};

  这是windows下实现线程同步最常见的封装。只需声明一个Lock实例,在需要同步的代码前后分别调用Enter和Leave即可。
  既然用起来这么简单,为什么还要继续改进?显然这种方法有个很大的缺陷,如果忘了调用Leave,或者在调用之前就return/throw退出,那么就会引起死锁。
  所以,我们需要类似auto_ptr的机制,自动维护栈数据的创建和删除。就暂且称它_auto_lock吧。
struct _auto_lock {
Lock& _lock;

_auto_lock(Lock& lock) : _lock(lock) {
_lock.Enter();
}

~_auto_lock() {
_lock.Leave();
}
};

  _auto_lock通过引用一个Lock实例来初始化,并立即锁住临界区;被销毁时则释放锁。

  有了这个机制,我们再也不用担心忘了调用.Leave()。只需提供一个Lock对象,就能在当前语块自动加锁解锁。再也不用担心死锁的问题了。
Lock mylock;

void Test()
{
// code1 ...

// syn code
{
_auto_lock x(mylock);
}

// code2 ...
}


  进入syn code的"{"之后,_auto_lock被构造;无论用那种方式离开"}",析构函数都会被调用。
  上述代码类似的在stl和boost里都是及其常见的。利用栈对象的构造/析构函数维护局部资源,算是C++很常用的一技巧。
  我们的目标又近了一步。下面开始利用经典的宏定义,制造一颗synchronized语法糖,最终实现这样的语法:
Lock mylock;

void Test()
{
// code1 ...

synchronized(mylock)
{
// sync code
}

// code2 ...
}

  显然需要一个叫synchronized宏,并且在里面定义_auto_lock。
#define synchronized(lock)        ..... _auto_lock _x(lock) ......


  乍一看这语法很像循环,并且要在循环内定义变量,所以用for(;;)的结构是再好不过了。
for(_auto_lock _x(mylock); ; )


  不过sync code我们只需执行一次,所以还需另一个变量来控制次数。由于for里面只能声明一种类型的变量,所以我们在外面再套一层循环:
for(int _i=0; _i<1; _i++)for(_auto_lock _x(mylock); _i<1; _i++)

  synchronized宏将mylock替换成上述代码,既没有违反语法,也实现相同的流程。得益于循环语法,甚至可以在synchronized内使用break来跳出同步块!

  我们将上述代码整理下,并做个简单的测试。
#include <stdio.h>
#include <windows.h>
#include <process.h>



struct Lock : CRITICAL_SECTION {
Lock() {
::InitializeCriticalSection(this);
}

~Lock() {
::DeleteCriticalSection(this);
}

void Enter() {
::EnterCriticalSection(this);
}

void Leave() {
::LeaveCriticalSection(this);
}
};

struct _auto_lock {
Lock& _lock;

_auto_lock(Lock& lock) : _lock(lock) {
_lock.Enter();
}

~_auto_lock() {
_lock.Leave();
}
};

#define synchronized(lock) for(int _i=0; _i<1; _i++)for(_auto_lock _x(lock); _i<1; _i++)






// ---------- demo ----------
Lock mylock;


// ---------- test1 ----------
void WaitTest(int id)
{
printf("No.%d waiting...\n", id);

synchronized(mylock)
{
Sleep(1000);
}

printf("No.%d done\n", id);
}

void Test1()
{
_beginthread((void(__cdecl*)(void*))WaitTest, 0, (void*) 1);
_beginthread((void(__cdecl*)(void*))WaitTest, 0, (void*) 2);
_beginthread((void(__cdecl*)(void*))WaitTest, 0, (void*) 3);
}





// ---------- test2 ----------
void ThrowFunc(int id)
{
printf("No.%d waiting...\n", id);

synchronized(mylock)
{
Sleep(1000);
throw "some err";
}

printf("No.%d done\n", id);
}

void ThrowTest(int id)
{
try
{
ThrowFunc(id);
}
catch(...)
{
printf("%d excepted\n", id);
}
}

void Test2()
{
_beginthread((void(__cdecl*)(void*))ThrowTest, 0, (void*) 1);
_beginthread((void(__cdecl*)(void*))ThrowTest, 0, (void*) 2);
_beginthread((void(__cdecl*)(void*))ThrowTest, 0, (void*) 3);
}



// ---------- test3 ----------
void BreakTest(int id)
{
printf("No.%d waiting...\n", id);

synchronized(mylock)
{
Sleep(1000);
break;
Sleep(99999999);
}

printf("No.%d done\n", id);
}

void Test3()
{
_beginthread((void(__cdecl*)(void*))BreakTest, 0, (void*) 1);
_beginthread((void(__cdecl*)(void*))BreakTest, 0, (void*) 2);
_beginthread((void(__cdecl*)(void*))BreakTest, 0, (void*) 3);
}




int main(int argc, char* argv[])
{
printf("Wait Test. Press any key to start...\n");
getchar();
Test1();

getchar();
printf("Exception Test. Press any key to start...\n");
getchar();
Test2();

getchar();
printf("Break Test. Press any key to start...\n");
getchar();
Test3();

getchar();
return 0;
}


  使用语法糖除了好看外,有个最重要的功能就是可以在synchronized同步块里使用break来跳出,并且不会引起死锁,这是其他方法无法实现的。
...全文
297 5 打赏 收藏 转发到动态 举报
写回复
用AI写文章
5 条回复
切换为时间正序
请发表友善的回复…
发表回复
漫步者、 2013-11-01
  • 打赏
  • 举报
回复
falao1 2013-11-01
  • 打赏
  • 举报
回复
厉害,受教了
  • 打赏
  • 举报
回复
还有多远 2013-11-01
  • 打赏
  • 举报
回复
思路清晰,语言明了,好厉害的说
赵4老师 2013-01-14
  • 打赏
  • 举报
回复
《Windows核心编程》 《深入解析Windows操作系统-Windows Internals》 Synchronization Functions The following functions are used in synchronization. CancelWaitableTimer CreateEvent CreateMutex CreateSemaphore CreateWaitableTimer DeleteCriticalSection EnterCriticalSection GetOverlappedResult InitializeCriticalSection InitializeCriticalSectionAndSpinCount InterlockedCompareExchange InterlockedDecrement InterlockedExchange InterlockedExchangeAdd InterlockedIncrement LeaveCriticalSection MsgWaitForMultipleObjects MsgWaitForMultipleObjectsEx OpenEvent OpenMutex OpenSemaphore OpenWaitableTimer PulseEvent QueueUserAPC ReleaseMutex ReleaseSemaphore ResetEvent SetCriticalSectionSpinCount SetEvent SetWaitableTimer SignalObjectAndWait TimerAPCProc TryEnterCriticalSection WaitForMultipleObjects WaitForMultipleObjectsEx WaitForSingleObject WaitForSingleObjectEx

64,654

社区成员

发帖
与我相关
我的任务
社区描述
C++ 语言相关问题讨论,技术干货分享,前沿动态等
c++ 技术论坛(原bbs)
社区管理员
  • C++ 语言社区
  • encoderlee
  • paschen
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
  1. 请不要发布与C++技术无关的贴子
  2. 请不要发布与技术无关的招聘、广告的帖子
  3. 请尽可能的描述清楚你的问题,如果涉及到代码请尽可能的格式化一下

试试用AI创作助手写篇文章吧