为什么很多人都用CRITICAL_SECTION实现锁

翅膀又硬了 2013-02-04 02:03:20
如题。CRITICAL_SECTION只能用于不同线程的互斥,而不能用于同步。因为同一个线程可以多次EnterCriticalSection。。 为什么网上的代码,很多“锁”、“自动锁”的类,都是用CRITICAL_SECTION实现。 如果使用的人不熟悉这个,很容易就会用错呀。
...全文
5902 26 打赏 收藏 转发到动态 举报
写回复
用AI写文章
26 条回复
切换为时间正序
请发表友善的回复…
发表回复
whdugh 2014-09-19
  • 打赏
  • 举报
回复
引用 1 楼 yunchao630 的回复:
这是我找的一份代码

/*********************************/
#ifdef _WIN32
#include <Windows.h>
#else
#include <pthread.h>
#endif

#ifdef _WIN32
#define LOCK_MUTEXT CRITICAL_SECTION
#else
#define LOCK_MUTEXT pthread_mutex_t
#endif

class CThreadLock
{
public:
	CThreadLock(void){
		Init();
	}
	~CThreadLock(){
		Close();
	}
	void Lock(){
#ifdef _WIN32
		EnterCriticalSection(&m_lock);
#else
		pthread_mutex_lock(&m_lock);
#endif
	}
	void UnLock(){
#ifdef _WIN32
		LeaveCriticalSection(&m_lock);
#else
		pthread_mutex_unlock(&m_lock);
#endif
	}
//protected:
private:
	LOCK_MUTEXT m_lock;
	void Init(){
#ifdef _WIN32
InitializeCriticalSection(&m_lock);
#else
		pthread_mutex_init(&m_lock);
#endif
	}
	void Close(){
#ifdef _WIN32
		DeleteCriticalSection(&m_lock);
#else
		pthread_mutex_destroy(&m_lock);
#endif
	}
};

//定义一个自动加锁的类
CAutoLock{
public:
	CAutoLock(CThreadLock *pThreadLock){
		m_pThreadLock=pThreadLock;
		if (NULL!=m_pThreadLock)
		{
			m_pThreadLock->Lock();
		}
	}
	~CAutoLock(){
		if (NULL!=m_pThreadLock)
		{
			m_pThreadLock->UnLock();
		}
	}
private:
	CThreadLock * m_pThreadLock;
};
/*********************************/
#endif
测试了上面的代码,设置了个全局变量,如何在两个函数里面调用CThreadLock的lock和unlock,发现在第个函数里锁不住这个全局变量啊,如何第一个函数还没有解锁,第二个函数就进入到临界区了,这问题纠结一天了,就指点啊
xiaolomg 2013-09-06
  • 打赏
  • 举报
回复
引用 3 楼 yunchao630 的回复:
[quote=引用 2 楼 zilaishuichina 的回复:] CRITICAL_SECTION本来就是用来 在多线程的情况下 保证一段代码一次只会被一个线程执行 同一个线程的多次EnterCriticalSection,必然是先后执行的,会有什么问题?
假如你有一个数据库连接池。该连接池有一个“获取一个空闲连接”的对外接口,这个函数肯定需要加锁。如果锁用CRITICAL_SECTION实现,外部某个线程用你这个连接池,假如他需要俩连接就可能会有问题。[/quote] 没问题啊,那连接就一个个地给,你理解的同步 难道是并发的意思?
wanghy1979 2013-02-11
  • 打赏
  • 举报
回复
我觉得楼猪可以看看《Windows环境下32位汇编语言程序设计》
Tishion 2013-02-06
  • 打赏
  • 举报
回复
引用 21 楼 wjb_yd 的回复:
引用 11 楼 yunchao630 的回复:引用 10 楼 wjb_yd 的回复:那楼主打算用什么实现锁呢? critical_section效率要高一些,因为是针对同一个进程的。 mutex是针对整个操作系统的。mutex也是不能用于同步的。信号量和事件才能做到同步。我只是想看看大家都想法。最近看了一遍同步和互斥,突然想到的这些。。。 你确定你理解“同步”的……
看错了,深表歉意。 但是还是建议发帖要质量。觉得别人的回复值得质疑就应该直接指出来,并且表述下自己的观点。
Tishion 2013-02-06
  • 打赏
  • 举报
回复
引用 21 楼 wjb_yd 的回复:
引用 11 楼 yunchao630 的回复:引用 10 楼 wjb_yd 的回复:那楼主打算用什么实现锁呢? critical_section效率要高一些,因为是针对同一个进程的。 mutex是针对整个操作系统的。mutex也是不能用于同步的。信号量和事件才能做到同步。我只是想看看大家都想法。最近看了一遍同步和互斥,突然想到的这些。。。 你确定你理解“同步”的……
质疑我的观点的时候请说出你的观点。 回复你这种帖子浪费口水浪费时间浪费金钱。
wjb_yd 2013-02-06
  • 打赏
  • 举报
回复
引用 11 楼 yunchao630 的回复:
引用 10 楼 wjb_yd 的回复:那楼主打算用什么实现锁呢? critical_section效率要高一些,因为是针对同一个进程的。 mutex是针对整个操作系统的。mutex也是不能用于同步的。信号量和事件才能做到同步。我只是想看看大家都想法。最近看了一遍同步和互斥,突然想到的这些。。。
你确定你理解“同步”的意思了么。。。
Tishion 2013-02-05
  • 打赏
  • 举报
回复
引用 楼主 yunchao630 的回复:
如题。CRITICAL_SECTION只能用于不同线程的互斥,而不能用于同步。因为同一个线程可以多次EnterCriticalSection。。 为什么网上的代码,很多“锁”、“自动锁”的类,都是用CRITICAL_SECTION实现。 如果使用的人不熟悉这个,很容易就会用错呀。
什么叫做不能用于同步? 你所理解的同步是什么?? 同步是一个广泛概念,如果有N个线程,我想让这个N个线程按照一定的顺序去访问一个数据,我的这个需求就是要线程“同步”,所有的互斥,等待,异步执行等等都是可以为了实现一个目的而诞生的,这个目的就是“同步”。 同一个线程是可以多次EnterCriticalSection,但是系统会保证,如果有一个线程已经EnterCriticalSection成功了,那在这个线程LeaveCriticalSection之前,不会有第二个线程EnterCriticalSection,这就可以达到线程按照一定的顺序访问数据,这看起来也像互斥。 注意 Event(事件),Mutex(互斥量),Semaphore(信号量),WaitableTimer(可等待定时器)这些都是同步(Synchronization )对象,用于线程同步(Synchronization)。 至于你的问题,为什么常用临界区:因为上述同步对象都是内核对象,使用时会涉及到用户态和内核态的切换,所以开销比较大,而临界区是在用户态实现的一个轻量的同步机制,在使用时不会有频繁的内核态和用户态的切换,资源消耗很小,所以比较普遍。
赵4老师 2013-02-05
  • 打赏
  • 举报
回复
《Windows核心编程》
YahooLucas 2013-02-05
  • 打赏
  • 举报
回复
引用 楼主 yunchao630 的回复:
如题。CRITICAL_SECTION只能用于不同线程的互斥,而不能用于同步。因为同一个线程可以多次EnterCriticalSection。。 为什么网上的代码,很多“锁”、“自动锁”的类,都是用CRITICAL_SECTION实现。 如果使用的人不熟悉这个,很容易就会用错呀。
没错啊,很多时候用CRITICAL_SECTION 实现锁啊。锁的目的不就是对某一个资源的独享式占有。当然你也可以使用Mutex来实现锁。但是CRITICAL_SECTION是非内核对象(用户对象?不知怎么叫),Mutex是内核对象。内核对象的创建、释放、工作会比非内核对象慢,因为微软在实现这套机制时做了很多背后的工作,所以会慢。CRITICAL_SECTION的执行速度会更快,所以大家多数情况下使用CRITICAL_SECTION,除非需要跨进程来进行互斥或者锁才会使用Mutex。 这个链接讲的不错:http://blog.csdn.net/wwl33695/article/details/8349549
Feelnature 2013-02-04
  • 打赏
  • 举报
回复
我是个初级菜鸟,来看看大鸟们怎么写程序
东莞某某某 2013-02-04
  • 打赏
  • 举报
回复
使用临界区主要是因为高效,进不去的时候自旋,这里就有个dwSpinCount,是自旋次数,当达到这个值得时候,还进不去的话就要进入内核态挂起等待。切换到内核态较之线程context切换成本很高。 On multiprocessor systems, if the critical section is unavailable, the calling thread spin dwSpinCount times before performing a wait operation on a semaphore associated with the critical section。
Saingel 2013-02-04
  • 打赏
  • 举报
回复

#ifdef _WIN32
        EnterCriticalSection(&m_lock);
#else
        pthread_mutex_lock(&m_lock);
#endif
这种写法真蛋疼 建议搞个公共定义头,头里

#ifdef _WIN32
#define XXX_LOCK(x) EnterCriticalSection(&x);
#else
#define XXX_LOCK(x) pthread_mutex_lock(&x);
#endif
下面就直接XXX_LOCK(m_lock),不用那么多#ifdef,看起来也舒服
oniisama 2013-02-04
  • 打赏
  • 举报
回复
我记得EnterCriticalSection一开始会自旋,所以如果需要同步的操作时间比较短,那么CRITICAL_SECTION的效率更高
ChinaTek 2013-02-04
  • 打赏
  • 举报
回复
critical_section效率要高一些,因为是针对同一个进程的。 mutex是针对整个操作系统的。 ==>有道理
赵4老师 2013-02-04
  • 打赏
  • 举报
回复
仅供参考
//循环向a函数每次发送200个字节长度(这个是固定的)的buffer,
//a函数中需要将循环传进来的buffer,组成240字节(也是固定的)的新buffer进行处理,
//在处理的时候每次从新buffer中取两个字节打印
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <windows.h>
#include <process.h>
#include <io.h>
//Log{
#define MAXLOGSIZE 20000000
#define MAXLINSIZE 16000
#include <time.h>
#include <sys/timeb.h>
#include <stdarg.h>
char logfilename1[]="MyLog1.log";
char logfilename2[]="MyLog2.log";
static char logstr[MAXLINSIZE+1];
char datestr[16];
char timestr[16];
char mss[4];
CRITICAL_SECTION cs_log;
FILE *flog;
void Lock(CRITICAL_SECTION *l) {
    EnterCriticalSection(l);
}
void Unlock(CRITICAL_SECTION *l) {
    LeaveCriticalSection(l);
}
void LogV(const char *pszFmt,va_list argp) {
    struct tm *now;
    struct timeb tb;

    if (NULL==pszFmt||0==pszFmt[0]) return;
    _vsnprintf(logstr,MAXLINSIZE,pszFmt,argp);
    ftime(&tb);
    now=localtime(&tb.time);
    sprintf(datestr,"%04d-%02d-%02d",now->tm_year+1900,now->tm_mon+1,now->tm_mday);
    sprintf(timestr,"%02d:%02d:%02d",now->tm_hour     ,now->tm_min  ,now->tm_sec );
    sprintf(mss,"%03d",tb.millitm);
    printf("%s %s.%s %s",datestr,timestr,mss,logstr);
    flog=fopen(logfilename1,"a");
    if (NULL!=flog) {
        fprintf(flog,"%s %s.%s %s",datestr,timestr,mss,logstr);
        if (ftell(flog)>MAXLOGSIZE) {
            fclose(flog);
            if (rename(logfilename1,logfilename2)) {
                remove(logfilename2);
                rename(logfilename1,logfilename2);
            }
        } else {
            fclose(flog);
        }
    }
}
void Log(const char *pszFmt,...) {
    va_list argp;

    Lock(&cs_log);
    va_start(argp,pszFmt);
    LogV(pszFmt,argp);
    va_end(argp);
    Unlock(&cs_log);
}
//Log}
#define ASIZE    200
#define BSIZE    240
#define CSIZE      2
char Abuf[ASIZE];
char Cbuf[CSIZE];
CRITICAL_SECTION cs_HEX ;
CRITICAL_SECTION cs_BBB ;
struct FIFO_BUFFER {
    int  head;
    int  tail;
    int  size;
    char data[BSIZE];
} BBB;
int No_Loop=0;
void HexDump(int cn,char *buf,int len) {
    int i,j,k;
    char binstr[80];

    Lock(&cs_HEX);
    for (i=0;i<len;i++) {
        if (0==(i%16)) {
            sprintf(binstr,"%03d %04x -",cn,i);
            sprintf(binstr,"%s %02x",binstr,(unsigned char)buf[i]);
        } else if (15==(i%16)) {
            sprintf(binstr,"%s %02x",binstr,(unsigned char)buf[i]);
            sprintf(binstr,"%s  ",binstr);
            for (j=i-15;j<=i;j++) {
                sprintf(binstr,"%s%c",binstr,('!'<buf[j]&&buf[j]<='~')?buf[j]:'.');
            }
            Log("%s\n",binstr);
        } else {
            sprintf(binstr,"%s %02x",binstr,(unsigned char)buf[i]);
        }
    }
    if (0!=(i%16)) {
        k=16-(i%16);
        for (j=0;j<k;j++) {
            sprintf(binstr,"%s   ",binstr);
        }
        sprintf(binstr,"%s  ",binstr);
        k=16-k;
        for (j=i-k;j<i;j++) {
            sprintf(binstr,"%s%c",binstr,('!'<buf[j]&&buf[j]<='~')?buf[j]:'.');
        }
        Log("%s\n",binstr);
    }
    Unlock(&cs_HEX);
}
int GetFromRBuf(int cn,CRITICAL_SECTION *cs,FIFO_BUFFER *fbuf,char *buf,int len) {
    int lent,len1,len2;

    lent=0;
    Lock(cs);
    if (fbuf->size>=len) {
        lent=len;
        if (fbuf->head+lent>BSIZE) {
            len1=BSIZE-fbuf->head;
            memcpy(buf     ,fbuf->data+fbuf->head,len1);
            len2=lent-len1;
            memcpy(buf+len1,fbuf->data           ,len2);
            fbuf->head=len2;
        } else {
            memcpy(buf     ,fbuf->data+fbuf->head,lent);
            fbuf->head+=lent;
        }
        fbuf->size-=lent;
    }
    Unlock(cs);
    return lent;
}
void thdB(void *pcn) {
    char        *recv_buf;
    int          recv_nbytes;
    int          cn;
    int          wc;
    int          pb;

    cn=(int)pcn;
    Log("%03d thdB              thread begin...\n",cn);
    while (1) {
        Sleep(10);
        recv_buf=(char *)Cbuf;
        recv_nbytes=CSIZE;
        wc=0;
        while (1) {
            pb=GetFromRBuf(cn,&cs_BBB,&BBB,recv_buf,recv_nbytes);
            if (pb) {
                Log("%03d recv %d bytes\n",cn,pb);
                HexDump(cn,recv_buf,pb);
                Sleep(1);
            } else {
                Sleep(1000);
            }
            if (No_Loop) break;//
            wc++;
            if (wc>3600) Log("%03d %d==wc>3600!\n",cn,wc);
        }
        if (No_Loop) break;//
    }
}
int PutToRBuf(int cn,CRITICAL_SECTION *cs,FIFO_BUFFER *fbuf,char *buf,int len) {
    int lent,len1,len2;

    Lock(cs);
    lent=len;
    if (fbuf->size+lent>BSIZE) {
        lent=BSIZE-fbuf->size;
    }
    if (fbuf->tail+lent>BSIZE) {
        len1=BSIZE-fbuf->tail;
        memcpy(fbuf->data+fbuf->tail,buf     ,len1);
        len2=lent-len1;
        memcpy(fbuf->data           ,buf+len1,len2);
        fbuf->tail=len2;
    } else {
        memcpy(fbuf->data+fbuf->tail,buf     ,lent);
        fbuf->tail+=lent;
    }
    fbuf->size+=lent;
    Unlock(cs);
    return lent;
}
void thdA(void *pcn) {
    char        *send_buf;
    int          send_nbytes;
    int          cn;
    int          wc;
    int           a;
    int          pa;

    cn=(int)pcn;
    Log("%03d thdA              thread begin...\n",cn);
    a=0;
    while (1) {
        Sleep(100);
        memset(Abuf,a,ASIZE);
        a=(a+1)%256;
        if (16==a) {No_Loop=1;break;}//去掉这句可以让程序一直循环直到按Ctrl+C或Ctrl+Break或当前目录下存在文件No_Loop
        send_buf=(char *)Abuf;
        send_nbytes=ASIZE;
        Log("%03d sending %d bytes\n",cn,send_nbytes);
        HexDump(cn,send_buf,send_nbytes);
        wc=0;
        while (1) {
            pa=PutToRBuf(cn,&cs_BBB,&BBB,send_buf,send_nbytes);
            Log("%03d sent %d bytes\n",cn,pa);
            HexDump(cn,send_buf,pa);
            send_buf+=pa;
            send_nbytes-=pa;
            if (send_nbytes<=0) break;//
            Sleep(1000);
            if (No_Loop) break;//
            wc++;
            if (wc>3600) Log("%03d %d==wc>3600!\n",cn,wc);
        }
        if (No_Loop) break;//
    }
}
int main() {
    InitializeCriticalSection(&cs_log );
    Log("Start===========================================================\n");
    InitializeCriticalSection(&cs_HEX );
    InitializeCriticalSection(&cs_BBB );

    BBB.head=0;
    BBB.tail=0;
    BBB.size=0;

    _beginthread((void(__cdecl *)(void *))thdA,0,(void *)1);
    _beginthread((void(__cdecl *)(void *))thdB,0,(void *)2);

    if (!access("No_Loop",0)) {
        remove("No_Loop");
        if (!access("No_Loop",0)) {
            No_Loop=1;
        }
    }
    while (1) {
        Sleep(1000);
        if (No_Loop) break;//
        if (!access("No_Loop",0)) {
            No_Loop=1;
        }
    }
    Sleep(3000);
    DeleteCriticalSection(&cs_BBB );
    DeleteCriticalSection(&cs_HEX );
    Log("End=============================================================\n");
    DeleteCriticalSection(&cs_log );
    return 0;
}
翅膀又硬了 2013-02-04
  • 打赏
  • 举报
回复
引用 10 楼 wjb_yd 的回复:
那楼主打算用什么实现锁呢? critical_section效率要高一些,因为是针对同一个进程的。 mutex是针对整个操作系统的。
mutex也是不能用于同步的。信号量和事件才能做到同步。我只是想看看大家都想法。最近看了一遍同步和互斥,突然想到的这些。。。
wjb_yd 2013-02-04
  • 打赏
  • 举报
回复
那楼主打算用什么实现锁呢? critical_section效率要高一些,因为是针对同一个进程的。 mutex是针对整个操作系统的。
赵4老师 2013-02-04
  • 打赏
  • 举报
回复
《30天自制操作系统》
苹果皮 2013-02-04
  • 打赏
  • 举报
回复
CriticalSection本来就是实现 一块资源只被一个线程使用的 比如说 A线程占用了那块资源 那么B线程就只能等A线程离开后B才能访问 如果LZ是想说 A,B线程都能同时访问的话 这可能就需要用到信号量来解决了。 不过应该只限于只读操作。。
zilaishuichina 2013-02-04
  • 打赏
  • 举报
回复
引用 5 楼 yunchao630 的回复:
引用 4 楼 zilaishuichina 的回复:引用 3 楼 yunchao630 的回复: 引用 2 楼 zilaishuichina 的回复:CRITICAL_SECTION本来就是用来 在多线程的情况下 保证一段代码一次只会被一个线程执行 同一个线程的多次EnterCriticalSection,必然是先后执行的,会有什么问题?假如你有一个数据库连……
异步的代码 也总有它需要顺序执行的地方 程序的执行流程无外乎顺序、分支、循环,总逃不出这三样的 所谓异步的实现,无外乎是 1、消息队列实现异步,逻辑需要的操作压入队列,一个一个的处理,处理完的callback。 2、链表定时轮询,处理完的callback。 不管是链表也好,队列也好,或者其他专门设计的容器也罢,那么只要这个容器有锁,不就又变回还是顺序的先后执行的情况了,异步的也没问题啊。
加载更多回复(6)

64,661

社区成员

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

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