能力值:
(RANK:1130 )
|
-
-
26 楼
NT6的那个读写锁,MSDN里面写了,不支持重入
读写锁重入,非常容易死锁
其实,普通锁也不应该设计成可重入,如果应用层需要锁重入,那还是重新设计应用层吧,锁重入,出BUG的可能性要增加至少80%
不支持锁重入是应该的,其实我个人认为,非特殊应用,连try都可以避免掉
linux没有TLS吧?
隐藏BUG,就是指根本说不出来的BUG,以前碰到过很多,后来换了架构,就没有了。设计锁这个东西,逻辑越复杂,BUG越难找
|
能力值:
( LV3,RANK:30 )
|
-
-
27 楼
重入问题,这个是不可避免的。除非所有的东西都是你自己一个人搞。工程变大而且多个对象用一个锁,多个人完成项目时,不允许重入的话,会是噩梦的。而且多数时候,也不会允许你重构
|
能力值:
(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);
}
|
能力值:
(RANK:1130 )
|
-
-
29 楼
重入问题肯定可以避免,如果有重入问题,说明架构设计的不好
你可以描述一下需求,我可以帮你避免重入
|
能力值:
( LV3,RANK:30 )
|
-
-
30 楼
不是说没法找到方法避免,而是整个设计根本不会允许你有可能重构,比如积淀了10年的代码,随便哪个人都不会允许你随便重构……即使问题再多也一样
而且,只要不要重入逻辑,我这个代码立马可以少至少一半
|
能力值:
( LV2,RANK:10 )
|
-
-
31 楼
看起来这个锁要cpu不停空转来获取写锁
|
能力值:
( LV3,RANK:30 )
|
-
-
32 楼
至于复杂与简单,还是要看具体需求,没有什么绝对标准……比如,只要不考虑重入逻辑,这份代码长度至少减一半……之前没没考虑重入问题时候,我这个锁确实就只有一百来行就搞定……看起来只加了个重入的需求,但是逻辑会马上复杂很多
|
能力值:
( LV13,RANK:400 )
|
-
-
33 楼
其他的不跟你争论,但是内存泄露啊!
赶紧的,充话费~嘿嘿
|
能力值:
(RANK:1130 )
|
-
-
34 楼
读写锁的应用情景本来就是读的频率远远大于写的频率,为了保证读的效率,而设计出来的
写的时候,浪费一点点CPU,不是什么大问题
当然,设计成信号量也不复杂
|
能力值:
( LV15,RANK:670 )
|
-
-
35 楼
LINUX 有 TLS 的,而且现在的系统基本上都有 NPTL 来实现线程:
pthread_key_create
pthread_getspecific
pthread_setspecific
pthread_key_delete
|
能力值:
(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);
}
|
能力值:
( LV3,RANK:30 )
|
-
-
37 楼
嗯,刚下飞机,才看到……虽然你说的那个地方,基本没可能会走到……不过理论上是这样……笔误吧……手机号私信给我
|
能力值:
( LV13,RANK:400 )
|
-
-
38 楼
毛线啊
pRwLock->pLastAcquireType=pLastType;
每次赋值之前你都没有释放内存,这怎么能说是基本没可能走到?!!
开玩笑的,我不差话费
|
能力值:
( LV3,RANK:30 )
|
-
-
39 楼
你反应错地方了吧……我那个每次都是记录最后一个节点,同时把上一个节点放到pre里
删除时做反向操作,同时把最后一个删掉(pLastType->pPre=pRwLock->pLastAcquireType,然后pRwLock->pLastAcquireType=pLastType)……哪里有问题了……
不过之前到真有个地方有问题,就是我说的那个基本没可能走到的逻辑……我还以为你看到了呢……原来没有
|
能力值:
( LV13,RANK:400 )
|
-
-
40 楼
我戳,手机看代码能看到这已经不错了,那就没问题了
|
能力值:
( LV2,RANK:10 )
|
-
-
41 楼
都是大牛人啊。谢谢楼主分享
|
能力值:
( LV3,RANK:30 )
|
-
-
42 楼
我是说,我这种do...while的习惯,内存泄露这种问题的可能性还是很小的
|
能力值:
( 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行
|
能力值:
( LV3,RANK:20 )
|
-
-
44 楼
附上测试代码
|
能力值:
( LV3,RANK:30 )
|
-
-
45 楼
[QUOTE=sjh_pediy;1257654]
附上测试代码[/QUOTE]
不要主动去unlock
因为你的类里面主动去调用了一次unlock,析构时又再调用一次。。。。。这个为啥不挂。。。 正常的用法是:
WLock WriteLock(g_RwLock);
如果要限制锁生效的范围,直接使用{}做控制就是了。。。。。
而且C++编码习惯一般也推荐像我说的这么做。。。。比如boost那些锁,基本上都是不需要你主动去unlock的,只要你自己控制锁变量的作用范围就够了
|
能力值:
( LV3,RANK:30 )
|
-
-
46 楼
[QUOTE=sjh_pediy;1257654]
附上测试代码[/QUOTE]
给你修改过的
同时把之前的wait函数改成了带ex版本,避免线程被APC意外唤醒,
同时改了提醒方式,打印出的是当时读和写线程的个数
理论上,每次打印出的数字,读或者写必有一个为0,而且如果写不为0那一定只能是1,否则锁逻辑有问题
|
能力值:
( LV3,RANK:20 )
|
-
-
47 楼
好的,我试试看
暂时看是没啥问题了,需要其它同学继续测试!
|
|
|