首页
社区
课程
招聘
[原创]原创读写锁,求测试
发表于: 2014-1-20 12:11 14034

[原创]原创读写锁,求测试

2014-1-20 12:11
14034
收藏
免费 0
支持
分享
最新回复 (46)
雪    币: 7309
活跃值: (3788)
能力值: (RANK:1130 )
在线值:
发帖
回帖
粉丝
26
NT6的那个读写锁,MSDN里面写了,不支持重入

读写锁重入,非常容易死锁

其实,普通锁也不应该设计成可重入,如果应用层需要锁重入,那还是重新设计应用层吧,锁重入,出BUG的可能性要增加至少80%

不支持锁重入是应该的,其实我个人认为,非特殊应用,连try都可以避免掉

linux没有TLS吧?

隐藏BUG,就是指根本说不出来的BUG,以前碰到过很多,后来换了架构,就没有了。设计锁这个东西,逻辑越复杂,BUG越难找
2014-1-26 12:51
0
雪    币: 65
活跃值: (112)
能力值: ( LV3,RANK:30 )
在线值:
发帖
回帖
粉丝
27
重入问题,这个是不可避免的。除非所有的东西都是你自己一个人搞。工程变大而且多个对象用一个锁,多个人完成项目时,不允许重入的话,会是噩梦的。而且多数时候,也不会允许你重构
2014-1-26 12:58
0
雪    币: 7309
活跃值: (3788)
能力值: (RANK:1130 )
在线值:
发帖
回帖
粉丝
28
我给个我写的windows平台下精简版的SRWLock

不支持重入


#pragma pack(push, 8)
typedef struct _MYSRWLOCK
{
	CRITICAL_SECTION csWriter;
	void* pSharedCount;
} MYSRWLOCK;
typedef MYSRWLOCK *PMYSRWLOCK;
#pragma pack(pop)

static void MyInitializeSRWLock(PMYSRWLOCK srwlock)
{
	InitializeCriticalSectionAndSpinCount(&srwlock->csWriter, 0xFA0);
	srwlock->pSharedCount = _aligned_malloc(sizeof(LONG), MEMORY_ALLOCATION_ALIGNMENT);
	_InterlockedExchange((volatile LONG*)srwlock->pSharedCount, 0);
}
static void MyFreeSRWLock(PMYSRWLOCK srwlock)
{
	DeleteCriticalSection(&srwlock->csWriter);
	_aligned_free(srwlock->pSharedCount);
}
class SRWLockShared
{
public:
	static void Acquire(PMYSRWLOCK srwlock)
	{
		EnterCriticalSection(&srwlock->csWriter);
		_InterlockedIncrement((volatile LONG*)srwlock->pSharedCount);
		LeaveCriticalSection(&srwlock->csWriter);
	}
	static void Release(PMYSRWLOCK srwlock)
	{
		_InterlockedDecrement((volatile LONG*)srwlock->pSharedCount);
	}
    SRWLockShared(PMYSRWLOCK srwlock) : m_srwlock(srwlock)
    {
		Acquire(m_srwlock);
    }

    ~SRWLockShared()
    {
		Release(m_srwlock);
    }

private:
    PMYSRWLOCK m_srwlock;
};

class SRWLockExclusive
{
public:
	static void Acquire(PMYSRWLOCK srwlock)
	{
		EnterCriticalSection(&srwlock->csWriter);
		while(_InterlockedCompareExchange((volatile LONG*)srwlock->pSharedCount, -1, 0) != 0);
	}
	static void Release(PMYSRWLOCK srwlock)
	{
		_InterlockedIncrement((volatile LONG*)srwlock->pSharedCount);
		LeaveCriticalSection(&srwlock->csWriter);
	}
    SRWLockExclusive(PMYSRWLOCK srwlock) : m_srwlock(srwlock)
    {
		Acquire(m_srwlock);
    }
    ~SRWLockExclusive()
    {
		Release(m_srwlock);
    }

private:
    PMYSRWLOCK m_srwlock;
};



用法:

MYSRWLOCK m_lock;

初始化
MyInitializeSRWLock(&m_lock);

销毁
MyFreeSRWLock(&m_lock);


共享锁
{
SRWLockShared lock(&m_lock);
}

互斥锁
{
SRWLockExclusive lock(&m_lock);
}




2014-1-26 12:59
0
雪    币: 7309
活跃值: (3788)
能力值: (RANK:1130 )
在线值:
发帖
回帖
粉丝
29
重入问题肯定可以避免,如果有重入问题,说明架构设计的不好

你可以描述一下需求,我可以帮你避免重入
2014-1-26 13:00
0
雪    币: 65
活跃值: (112)
能力值: ( LV3,RANK:30 )
在线值:
发帖
回帖
粉丝
30
不是说没法找到方法避免,而是整个设计根本不会允许你有可能重构,比如积淀了10年的代码,随便哪个人都不会允许你随便重构……即使问题再多也一样

而且,只要不要重入逻辑,我这个代码立马可以少至少一半
2014-1-26 13:03
0
雪    币: 209
活跃值: (143)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
31
看起来这个锁要cpu不停空转来获取写锁
2014-1-26 13:11
0
雪    币: 65
活跃值: (112)
能力值: ( LV3,RANK:30 )
在线值:
发帖
回帖
粉丝
32
至于复杂与简单,还是要看具体需求,没有什么绝对标准……比如,只要不考虑重入逻辑,这份代码长度至少减一半……之前没没考虑重入问题时候,我这个锁确实就只有一百来行就搞定……看起来只加了个重入的需求,但是逻辑会马上复杂很多
2014-1-26 13:18
0
雪    币: 496
活跃值: (286)
能力值: ( LV13,RANK:400 )
在线值:
发帖
回帖
粉丝
33
其他的不跟你争论,但是内存泄露啊!
赶紧的,充话费~嘿嘿
2014-1-26 13:33
0
雪    币: 7309
活跃值: (3788)
能力值: (RANK:1130 )
在线值:
发帖
回帖
粉丝
34
读写锁的应用情景本来就是读的频率远远大于写的频率,为了保证读的效率,而设计出来的
写的时候,浪费一点点CPU,不是什么大问题
当然,设计成信号量也不复杂
2014-1-26 14:00
0
雪    币: 1708
活跃值: (586)
能力值: ( LV15,RANK:670 )
在线值:
发帖
回帖
粉丝
35
LINUX 有 TLS 的,而且现在的系统基本上都有 NPTL 来实现线程:
pthread_key_create
pthread_getspecific
pthread_setspecific
pthread_key_delete
2014-1-26 14:08
0
雪    币: 7309
活跃值: (3788)
能力值: (RANK:1130 )
在线值:
发帖
回帖
粉丝
36
个人认为还不错的共享锁架构
可以解决31楼说的空转问题,依然不建议重入



// 读写锁
#if !defined(_WIN32) && !defined(_WIN64)

#  include <pthread.h>

typedef pthread_mutex_t Lock_type;
typedef pthread_cond_t WaitCondition;
typedef pthread_t ThreadHandle;

#  define lock_init(x) pthread_mutex_init(&(x), NULL)
#  define lock_grab(x) pthread_mutex_lock(&(x))
#  define lock_release(x) pthread_mutex_unlock(&(x))
#  define lock_destroy(x) pthread_mutex_destroy(&(x))

#  define cond_init(x) pthread_cond_init(&(x), NULL)
#  define cond_destroy(x) pthread_cond_destroy(&(x))
#  define cond_signal(x) pthread_cond_signal(&(x))
#  define cond_timedwait(x,y,z) pthread_cond_timedwait(&(x),&(y),z)
#  define cond_wait(x,y) pthread_cond_wait(&(x),&(y))

#  define sem_init(x) pthread_cond_init(&(x), NULL)
#  define sem_destroy(x) pthread_cond_destroy(&(x))
#  define sem_signal_one(x) pthread_cond_signal(&(x))
#  define sem_signal_all(x) pthread_cond_broadcast(&(x))
#  define sem_timedwait(x,y,z) pthread_cond_timedwait(&(x),&(y),z)
#  define sem_wait(x,y) pthread_cond_wait(&(x),&(y))


#  define thread_create(x,f,id) !pthread_create(&(x),NULL,(void* (*)(void*))f,&(id))
#  define thread_join(x) pthread_join(x, NULL)

#else

#include <Windows.h>
#include <limits.h>

typedef CRITICAL_SECTION Lock_type;
typedef HANDLE WaitCondition;
typedef HANDLE ThreadHandle;

#define lock_init(x) InitializeCriticalSection(&(x))
#define lock_grab(x) EnterCriticalSection(&(x))
#define lock_release(x) LeaveCriticalSection(&(x))
#define lock_destroy(x) DeleteCriticalSection(&(x))

#define cond_init(x) { x = CreateEvent(0, FALSE, FALSE, 0); }
#define cond_destroy(x) CloseHandle(x)
#define cond_signal(x) SetEvent(x)
#define cond_timedwait(x,y,z) { lock_release(y); WaitForSingleObject(x,z); lock_grab(y); }
#define cond_wait(x,y) { cond_timedwait((x), (y), INFINITE); }

#define sem_init(x) { x = CreateSemaphore(0, 0, LONG_MAX, 0); }
#define sem_destroy(x) { CloseHandle((x)); }
#define sem_signal(x,y) { ReleaseSemaphore((x), (y), 0); }
#define sem_signal_one(x) { sem_signal((x), 1); }
#define sem_signal_all(x) { sem_signal((x), LONG_MAX); }
#define sem_timedwait(x,y,z) { lock_release((y)); WaitForSingleObject((x), (z));lock_grab((y));  }
#define sem_wait(x,y) { sem_timedwait((x), (y), INFINITE); }

#define thread_create(x,f,t) (x = CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)f,(&t),0,NULL), x != NULL)
#define thread_join(x) { WaitForSingleObject(x, INFINITE); CloseHandle(x); }

#endif


typedef struct _swrlock_type
{
	// 互斥锁
	Lock_type lock;

	// 共享等待信号量
	WaitCondition shared_cond;

	// 独占等待信号量
	WaitCondition exclusive_cond;

	// 共享锁数目
	int shared_count;

	// 独占锁等待数目
	int exclusive_wait_count;

	// 是否独占状态
	bool exclusive;
}swrlock_type;

static void srwlock_init(swrlock_type* swrlock)
{
	lock_init(swrlock->lock);
	sem_init(swrlock->shared_cond);
	sem_init(swrlock->exclusive_cond);
	swrlock->shared_count = 0;
	swrlock->exclusive_wait_count = 0;
	swrlock->exclusive = false;
}
static void srwlock_destroy(swrlock_type* swrlock)
{
	sem_destroy(swrlock->exclusive_cond);
	sem_destroy(swrlock->shared_cond);
	lock_destroy(swrlock->lock);
}
static void srwlock_shared_grab(swrlock_type* swrlock)
{
	lock_grab(swrlock->lock);

	while(swrlock->exclusive || swrlock->exclusive_wait_count)
	{
		sem_wait(swrlock->shared_cond, swrlock->lock);
	}
	swrlock->shared_count ++;

	lock_release(swrlock->lock);
}
static void srwlock_shared_release(swrlock_type* swrlock)
{
	bool noshared;
	lock_grab(swrlock->lock);

	noshared = !(-- swrlock->shared_count);

	if(noshared)
	{
		sem_signal_one(swrlock->exclusive_cond);
		sem_signal_all(swrlock->shared_cond);
	}

	lock_release(swrlock->lock);
}
static void srwlock_exclusive_grab(swrlock_type* swrlock)
{
	lock_grab(swrlock->lock);

	swrlock->exclusive_wait_count ++;
	while(swrlock->exclusive || swrlock->shared_count)
	{
		sem_wait(swrlock->exclusive_cond, swrlock->lock);
	}
	swrlock->exclusive_wait_count --;
	swrlock->exclusive = true;

	lock_release(swrlock->lock);
}
static void srwlock_exclusive_release(swrlock_type* swrlock)
{
	lock_grab(swrlock->lock);

	swrlock->exclusive = false;
	sem_signal_one(swrlock->exclusive_cond);
	sem_signal_all(swrlock->shared_cond);

	lock_release(swrlock->lock);
}

2014-1-26 14:31
0
雪    币: 65
活跃值: (112)
能力值: ( LV3,RANK:30 )
在线值:
发帖
回帖
粉丝
37
嗯,刚下飞机,才看到……虽然你说的那个地方,基本没可能会走到……不过理论上是这样……笔误吧……手机号私信给我
2014-1-26 16:16
0
雪    币: 496
活跃值: (286)
能力值: ( LV13,RANK:400 )
在线值:
发帖
回帖
粉丝
38
毛线啊
pRwLock->pLastAcquireType=pLastType;
每次赋值之前你都没有释放内存,这怎么能说是基本没可能走到?!!
开玩笑的,我不差话费
2014-1-26 21:03
0
雪    币: 65
活跃值: (112)
能力值: ( LV3,RANK:30 )
在线值:
发帖
回帖
粉丝
39
你反应错地方了吧……我那个每次都是记录最后一个节点,同时把上一个节点放到pre里
删除时做反向操作,同时把最后一个删掉(pLastType->pPre=pRwLock->pLastAcquireType,然后pRwLock->pLastAcquireType=pLastType)……哪里有问题了……

不过之前到真有个地方有问题,就是我说的那个基本没可能走到的逻辑……我还以为你看到了呢……原来没有
2014-1-26 21:13
0
雪    币: 496
活跃值: (286)
能力值: ( LV13,RANK:400 )
在线值:
发帖
回帖
粉丝
40
我戳,手机看代码能看到这已经不错了,那就没问题了
2014-1-26 22:11
0
雪    币: 217
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
41
都是大牛人啊。谢谢楼主分享
2014-1-26 22:37
0
雪    币: 65
活跃值: (112)
能力值: ( LV3,RANK:30 )
在线值:
发帖
回帖
粉丝
42
我是说,我这种do...while的习惯,内存泄露这种问题的可能性还是很小的
2014-1-26 22:38
0
雪    币: 65
活跃值: (112)
能力值: ( LV3,RANK:30 )
在线值:
发帖
回帖
粉丝
43
再给个不考虑重入问题的,一样很简单:

PVOID __stdcall CreateRWLock()
{
	PRW_LOCK pLock=NULL;

	do 
	{
		pLock=(PRW_LOCK)malloc(sizeof(RW_LOCK));

		if (!pLock)
		{
			break;
		}

		RtlZeroMemory(pLock,sizeof(RW_LOCK));

		pLock->iNowReaderCount=0;

		pLock->hShareLock=CreateEvent(NULL,FALSE,TRUE,NULL);

		if (pLock->hShareLock)
		{
			InitializeCriticalSection(&pLock->ExclusiveLock);

			break;
		}

		free(pLock);

		pLock=NULL;

	} while (FALSE);

	return (PVOID)pLock;
}

VOID __stdcall DeleteRWLock(PVOID pLock)
{
	PRW_LOCK pRwLock=(PRW_LOCK)pLock;

	if (pRwLock->hShareLock)
	{
		DeleteCriticalSection(&pRwLock->ExclusiveLock);

		CloseHandle(pRwLock->hShareLock);
	}

	free(pLock);
}

VOID __stdcall AcquireShareLock(PVOID pLock)
{
	PRW_LOCK pRwLock=(PRW_LOCK)pLock;

	EnterCriticalSection(&pRwLock->ExclusiveLock);

	if(InterlockedIncrement(&pRwLock->iNowReaderCount)==1)
	{
		WaitForSingleObject(pRwLock->hShareLock,INFINITE);
	}

	LeaveCriticalSection(&pRwLock->ExclusiveLock);
}


VOID __stdcall ReleaseShareLock(PVOID pLock)
{
	PRW_LOCK pRwLock=(PRW_LOCK)pLock;

	if(InterlockedDecrement(&pRwLock->iNowReaderCount)==0)
	{
		SetEvent(pRwLock->hShareLock);
	}
}

VOID __stdcall AcquireExclusiveLock(PVOID pLock)
{
	PRW_LOCK pRwLock=(PRW_LOCK)pLock;

	EnterCriticalSection(&pRwLock->ExclusiveLock);//占用写锁,使得其它线程不可读(后续所有想获取读锁和写锁的操作,都会卡在这里)

	WaitForSingleObject(pRwLock->hShareLock,INFINITE);//等待所有已经获取读锁的线程完成操作(一定只有一个想获取写锁的线程在这里等待,因为其它想获取写锁的线程会卡在上面一行)

	SetEvent(pRwLock->hShareLock);
}



VOID __stdcall ReleaseExclusiveLock(PVOID pLock)
{
	PRW_LOCK pRwLock=(PRW_LOCK)pLock;

	LeaveCriticalSection(&pRwLock->ExclusiveLock);
}


一旦引入重入,复杂度就会几何级数增长了

这个代码,貌似都不到100行
2014-1-27 10:24
0
雪    币: 288
活跃值: (269)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
44

附上测试代码
上传的附件:
2014-1-27 13:07
0
雪    币: 65
活跃值: (112)
能力值: ( LV3,RANK:30 )
在线值:
发帖
回帖
粉丝
45
[QUOTE=sjh_pediy;1257654]
附上测试代码[/QUOTE]

不要主动去unlock

因为你的类里面主动去调用了一次unlock,析构时又再调用一次。。。。。这个为啥不挂。。。

正常的用法是:

WLock WriteLock(g_RwLock);

如果要限制锁生效的范围,直接使用{}做控制就是了。。。。。

而且C++编码习惯一般也推荐像我说的这么做。。。。比如boost那些锁,基本上都是不需要你主动去unlock的,只要你自己控制锁变量的作用范围就够了
2014-1-27 13:26
0
雪    币: 65
活跃值: (112)
能力值: ( LV3,RANK:30 )
在线值:
发帖
回帖
粉丝
46
[QUOTE=sjh_pediy;1257654]
附上测试代码[/QUOTE]

给你修改过的
同时把之前的wait函数改成了带ex版本,避免线程被APC意外唤醒,

同时改了提醒方式,打印出的是当时读和写线程的个数

理论上,每次打印出的数字,读或者写必有一个为0,而且如果写不为0那一定只能是1,否则锁逻辑有问题
上传的附件:
2014-1-27 15:03
0
雪    币: 288
活跃值: (269)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
47
好的,我试试看
暂时看是没啥问题了,需要其它同学继续测试!
2014-1-27 17:41
0
游客
登录 | 注册 方可回帖
返回
//