DWORD WINAPI Fun1Proc(
  LPVOID lpParameter   // thread data
)
{
while(TRUE)
{
WaitForSingleObject(hMutex,INFINITE);
if(tickets>0)
{
cout<<"thread1 sell ticket : "<<tickets--<<endl;
}
else
                    break;
ReleaseMutex(hMutex);
cout << "1 released" << endl;       
}
return 0;
}DWORD WINAPI Fun2Proc(
  LPVOID lpParameter   // thread data
)
{
while(TRUE)
{
WaitForSingleObject(hMutex,INFINITE);
if(tickets>0)
{
cout<<"thread2 sell ticket : "<<tickets--<<endl;
}
else
                    break;
ReleaseMutex(hMutex);
cout << "2 released" << endl;
}
return 0;
}为什么每次都release两次?多线程c++互斥对象

解决方案 »

  1.   

    你用的是VC6吧?
    另外线程函数中break之前也需要释放互斥量
      

  2.   

    哦,明白你的意思,就是执行完了else语句会直接跳出while,这样就没有release掉互斥对象。我照做了,结果还是如图那样。不知道为什么
      

  3.   

    #include <windows.h>
    #include <iostream.h>DWORD WINAPI Fun1Proc(
      LPVOID lpParameter   // thread data
    );DWORD WINAPI Fun2Proc(
      LPVOID lpParameter   // thread data
    );
    int index=0;
    int tickets=90;
    HANDLE hMutex;
    void main()
    {
    HANDLE hThread1;
    HANDLE hThread2;
    hMutex=CreateMutex(NULL,TRUE,"tickets");

    hThread1=CreateThread(NULL,0,Fun1Proc,NULL,0,NULL);
    hThread2=CreateThread(NULL,0,Fun2Proc,NULL,0,NULL);
    CloseHandle(hThread1);
    CloseHandle(hThread2); if(hMutex)
    {
    if(ERROR_ALREADY_EXISTS==GetLastError())
    {
    cout<<"only one instance can run!"<<endl;
    return;
    }
    }
    WaitForSingleObject(hMutex,INFINITE);
    ReleaseMutex(hMutex);
    ReleaseMutex(hMutex); Sleep(6000);
    }DWORD WINAPI Fun1Proc(
      LPVOID lpParameter   // thread data
    )
    {
    while(TRUE)
    {
    WaitForSingleObject(hMutex,INFINITE);
    if(tickets>0)
    {
    cout<<"thread1 sell ticket : "<<tickets--<<endl;
    }
    else
    break;
    ReleaseMutex(hMutex);
    cout << "1 released" << endl;      
    } return 0;
    }DWORD WINAPI Fun2Proc(
      LPVOID lpParameter   // thread data
    )
    {
    while(TRUE)
    {
    WaitForSingleObject(hMutex,INFINITE);
    if(tickets>0)
    {
    cout<<"thread2 sell ticket : "<<tickets--<<endl;
    }
    else
    break;
    ReleaseMutex(hMutex);
    cout << "2 released" << endl;
    }
    return 0;
    }
    以上就是完整的代码,就是孙鑫第十五章的程序,我在release后面多加了一条cout语句而已。
      

  4.   

    我是不明白为什么有两次release?在release后面加延时吗?
      

  5.   

    VS2008运行正常,没出现lz说的情况
      

  6.   

    早在:
    http://bbs.csdn.net/topics/390535912
    中回答了 , 还 不 结贴。
      

  7.   

    CS和Mutex用法不一样的吧。如果release后面没有任何语句,两个就是严格交替执行的。
      

  8.   

    CS和Mutex用法不一样的吧。如果release后面没有任何语句,两个就是严格交替执行的。并不是严格交替执行,而是凑巧交替执行而已。不能把程序的执行循序,让操作系统“随机”给你实现。
    “互斥”与“临界区”,如果不涉及进程间的同步,从程序逻辑的层面上来看,它们之间的区别不是很大。不同的编译器、不同的运行环境,都会影响执行的结果。比如在我的机器上,会出现
        "thread1 sell ticket : 12
        1 released 
        thread1 sell ticket : 11
        1 released 
        thread1 sell ticket : 10
        1 released 
        2 released "
      

  9.   

     WaitForSingleObject(hMutex,INFINITE);
        ReleaseMutex(hMutex);
        ReleaseMutex(hMutex);  此处
     
        Sleep(6000);
    楼主为什么要释放两遍互斥量???
      

  10.   

    CS和Mutex用法不一样的吧。如果release后面没有任何语句,两个就是严格交替执行的。并不是严格交替执行,而是凑巧交替执行而已。不能把程序的执行循序,让操作系统“随机”给你实现。
    “互斥”与“临界区”,如果不涉及进程间的同步,从程序逻辑的层面上来看,它们之间的区别不是很大。不同的编译器、不同的运行环境,都会影响执行的结果。比如在我的机器上,会出现
        "thread1 sell ticket : 12
        1 released 
        thread1 sell ticket : 11
        1 released 
        thread1 sell ticket : 10
        1 released 
        2 released "
    嗯。我师兄也是说凑巧这回事。是和单CPU,多CPU也有关系吧
      

  11.   

    因为主线程创建了一个互斥对象,最后必须由主线程来释放互斥对象。而主线程也在等待互斥对象,如果主线程使用完了互斥对象,也是应该释放的,以便通知其他线程互斥对象可用。
    给我的感觉是
    WaitForSingleObject(hMutex,INFINITE);
        ReleaseMutex(hMutex);
        ReleaseMutex(hMutex);
    这三行多余!
    更重要的是你没有关闭互斥量,应该把这三行去掉,在Sleep(6000);后面加上closehandle(hMutex);
    原因是主线程并不需要获得互斥量,他并没有和其他的线程使用共享资源,也就是说他不需要同步,按照你写的代码的话
    WaitForSingleObject(hMutex,INFINITE);  //获得互斥两
    ReleaseMutex(hMutex); //把获得的互斥量释放掉
    ReleaseMutex(hMutex);//再次释放互斥两,但没有获得怎么释放呢??会出问题吧。
      

  12.   

    通过我刚的分析,我又发现了一个问题,那就是当别的线程获得互斥量的时候,当切换到主线程的时候,主线程会把子线程获得的互斥量释放掉。原因就是你释放两次!
    不是吧。主线程的两个release都是因为主线程先创建了互斥对象,并且CreateMutex函数的第二个参数为TRUE,即互斥对象一开始被创建的时候就和主线程关联了,互斥对象内部的计数器加1。所以主线程必须要release掉才行。第二个release是因为主线程等待互斥对象,而此时互斥对象就已经和主线程关联着呢。所以互斥对象内部的计数器又加1,变成了2.所以release两次没有错的呀。不过我也觉得那三行貌似是多余。不,是那两行。
      

  13.   

    #include <windows.h>
    #include <iostream.h>
     
    DWORD WINAPI Fun1Proc(
      LPVOID lpParameter   // thread data
    );
     
    DWORD WINAPI Fun2Proc(
      LPVOID lpParameter   // thread data
    );
    int index=0;
    int tickets=90;
    HANDLE hMutex;
    void main()
    {
        HANDLE hThread1;
        HANDLE hThread2;
        hMutex=CreateMutex(NULL,TRUE,"tickets");
         
        hThread1=CreateThread(NULL,0,Fun1Proc,NULL,0,NULL);
        hThread2=CreateThread(NULL,0,Fun2Proc,NULL,0,NULL);
        CloseHandle(hThread1);
        CloseHandle(hThread2);
     
        if(hMutex)
        {
            if(ERROR_ALREADY_EXISTS==GetLastError())
            {
                cout<<"only one instance can run!"<<endl;
                return;
            }
        }
    //    WaitForSingleObject(hMutex,INFINITE);
    //    ReleaseMutex(hMutex);
        ReleaseMutex(hMutex);
     
        Sleep(6000);
    }
     
    DWORD WINAPI Fun1Proc(
      LPVOID lpParameter   // thread data
    )
    {
        while(TRUE)
        {
            WaitForSingleObject(hMutex,INFINITE);
            if(tickets>0)
            {
            cout<<"thread1 sell ticket : "<<tickets--<<endl;
            ReleaseMutex(hMutex);
            }
            else{
            ReleaseMutex(hMutex);
            break;
            }
            cout << "1 released" << endl;      
        }
     
        return 0;
    }
     
    DWORD WINAPI Fun2Proc(
      LPVOID lpParameter   // thread data
    )
    {
        while(TRUE)
        {
            WaitForSingleObject(hMutex,INFINITE);
            if(tickets>0)
            {
            cout<<"thread2 sell ticket : "<<tickets--<<endl;
            ReleaseMutex(hMutex);
            }
            else{
            ReleaseMutex(hMutex);
            break;
            }
            cout << "2 released" << endl;
        }
        return 0;
    }同志们,请问这个结果又该怎么解释啊?还是很迷糊啊
      

  14.   

    你确定你贴出的结果对用的是你贴的代码吗??按照你的代码没卖完一张票都是要输出release语句的,你的却没有。
    当然不是这段代码也没关系,两个线程release后的代码执行的时候是随机的,不受控制,给你的建议是:
     if(tickets>0)
            {
            cout<<"thread1 sell ticket : "<<tickets--<<endl;
            cout << "1 released" << endl;
            ReleaseMutex(hMutex);
            }
            else{
            cout << "1 released" << endl;
            ReleaseMutex(hMutex);
            break;
            }
           // cout << "1 released" << endl;      
    也就是在每一个release之前输出这句话。release之后输出是不可控制的。
      

  15.   

    你确定你贴出的结果对用的是你贴的代码吗??按照你的代码没卖完一张票都是要输出release语句的,你的却没有。
    当然不是这段代码也没关系,两个线程release后的代码执行的时候是随机的,不受控制,给你的建议是:
     if(tickets>0)
            {
            cout<<"thread1 sell ticket : "<<tickets--<<endl;
            cout << "1 released" << endl;
            ReleaseMutex(hMutex);
            }
            else{
            cout << "1 released" << endl;
            ReleaseMutex(hMutex);
            break;
            }
           // cout << "1 released" << endl;      
    也就是在每一个release之前输出这句话。release之后输出是不可控制的。
    嗯。我试过了在release之前cout,是没有问题的。可为什么release之后cout就不可控制了呢?
      

  16.   

    在多线程中,几个线程之间的
     WaitForSingleObject(hMutex,INFINITE);
     ·····
    ······//代码段
    ······      
     ReleaseMutex(hMutex);
    这段代码在宏观上是原子性操作的,也就是说进入到WaitForSingleObject(hMutex,INFINITE);之后就要一直执行的releaseMutex();你可以这么理解但微观上不是这样的,而且在执行这段代码的时候也是会切换走的,就算切换走他也只能执行互斥量之外的代码。
      

  17.   

    有时不写日志到文件中是无论如何也发现不了问题在哪里的,包括捕获各种异常、写日志到屏幕、单步或设断点或生成core文件、……这些方法都不行! 写日志到文件参考下面:
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #ifdef WIN32
        #include <windows.h>
        #include <io.h>
    #else
        #include <unistd.h>
        #include <sys/time.h>
        #include <pthread.h>
        #define  CRITICAL_SECTION   pthread_mutex_t
        #define  _vsnprintf         vsnprintf
    #endif
    //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;
    #ifdef WIN32
    void Lock(CRITICAL_SECTION *l) {
        EnterCriticalSection(l);
    }
    void Unlock(CRITICAL_SECTION *l) {
        LeaveCriticalSection(l);
    }
    #else
    void Lock(CRITICAL_SECTION *l) {
        pthread_mutex_lock(l);
    }
    void Unlock(CRITICAL_SECTION *l) {
        pthread_mutex_unlock(l);
    }
    #endif
    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}
    int main(int argc,char * argv[]) {
        int i;
    #ifdef WIN32
        InitializeCriticalSection(&cs_log);
    #else
        pthread_mutex_init(&cs_log,NULL);
    #endif
        for (i=0;i<10000;i++) {
            Log("This is a Log %04d from FILE:%s LINE:%d\n",i, __FILE__, __LINE__);
        }
    #ifdef WIN32
        DeleteCriticalSection(&cs_log);
    #else
        pthread_mutex_destroy(&cs_log);
    #endif
        return 0;
    }
    //1-78行添加到你带main的.c或.cpp的那个文件的最前面
    //81-85行添加到你的main函数开头
    //89-93行添加到你的main函数结束前
    //在要写LOG的地方仿照第87行的写法写LOG到文件MyLog1.log中
      

  18.   

    给你改了改:#include <windows.h>
    #include <stdio.h>int index=0;volatile int tickets=100;HANDLE g_hMutex=0;
     
    DWORD WINAPI Fun1Proc(LPVOID lpParameter)
    {
        while(tickets >0 )
        {
    WaitForSingleObject(g_hMutex,INFINITE);
    tickets--;
    printf("thread 1 sell ticket : %d\r\n", tickets);
            while((tickets %2) == 1)
            {
    printf("Wait thread 2 be scheduled\r\n");
            ReleaseMutex(g_hMutex);
    Sleep(0);
            }
    }
    // 
        return 0;
    }
     
    DWORD WINAPI Fun2Proc( LPVOID lpParameter)
    {
        while(tickets >0 )
        {
            WaitForSingleObject(g_hMutex,INFINITE);
    tickets--;
    printf("thread 2 sell ticket : %d\r\n", tickets);
            while((tickets %2) == 0)
            {
    printf("Wait thread 1 be scheduled\r\n");
            ReleaseMutex(g_hMutex);
    Sleep(0);
            }
        }
        return 0;
    }void main()
    {
        HANDLE hThread1;
        HANDLE hThread2;
    //
        g_hMutex = CreateMutex(NULL,TRUE,"tickets");
        ReleaseMutex(g_hMutex); 
    //
        hThread1=CreateThread(NULL,0,Fun1Proc,NULL,0,NULL);
        hThread2=CreateThread(NULL,0,Fun2Proc,NULL,0,NULL);
    //
        CloseHandle(hThread1);
        CloseHandle(hThread2);
     
        while (tickets > 0 );
    }
      

  19.   

    func1 是 检查 奇数, 所以 可以 改到 98,
    99 开始 要 检查 偶数。
    不是 问题 的 重点 !
      

  20.   

    我的程序, 初始化tickets 为 11 时,结果如下:
     
      

  21.   

    再说一遍,操作系统 愿意 叫 哪个线程运行 就 那个线程 运行,
    所以 一个线程 可以 被调度 一次,然后 又被调度,。现在线程程中有 同步 代码:
    while((tickets %2) == 1) // 奇偶 数
            {
    printf("Wait thread 2 be scheduled\r\n");
            ReleaseMutex(g_hMutex);
    Sleep(0);
            }
    所以出现 等待 多次。互斥 只是说 ,你用 我 不能用,我用 你 不能 用。
    不保证:我用我还用, 你用你还用即  00,11,01,10,  4 个状态 互斥只表示 10,01 
      

  22.   

    看了你的代码,唯一的解释就是cout不是线程安全的,需要自己去同步,你换成printf,如果是用VC6.0编译器,将编译选项设置为多线程
      

  23.   

    release两次,因为:那个线程连续两次得到运行机会,并连续两次抢到互斥量。,因为同一优先级的线程,并不是轮流调度的,而是哪个先抢到时间片哪个先调度.互斥量,也不保证线程轮流得到机会,而是大概会先到先得而已。系统只保证,每个同一优先级的线程的,都有可能获得同样的调度机会.
    PS:
    可能cout<<"thread2 sell ticket : "<<tickets--<<endl; 
    这种输入输出,本来就是同步的
    不然,不会输出的这么整齐。
      

  24.   


    你的程序只要一个线程退出,就会造成,互斥量死锁,

    如果不执行ReleaseMutex(hMutex);当线程1的时间片到了,CPU也会强制让线程1终止啊,那也会强制的将互斥量的状态变为可用的吧?所以不执行这句,应该也不会死锁的吧。
      

  25.   

    ReleaseMutex(hMutex);执行后,hMutex 才可用,如果时间片到了,ReleaseMutex(hMutex);
    没有执行虽然,会执行线程切换,hMutex 依然不可用。
    时间到,只会让系统有机会检查,是否有其他线程可以运行。不是一定会运行其它线程。
    任何一个正在等待资源,
    而没有到达 1)等到资源,2) 等待超时 3) 等待出错(本例中 hMutex已经不存在了)
    3 个条件之一的线程
    始终没有机会运行。
    时间片只是系统调度的一个手段。时间到,系统重新安排一个线程,在某个内核上运行
    ---可能是同一个线程,也可能是其他线程----Windows 的 调度和运行单位是线程,
    进程只是线程的容器。所以,一个线程停止运行,不一定安排进程中的其它线程运行,整个系统中的所有线程,按照优先级和状态,完全挂起的不调度(等待中或者睡眠中),只有就绪的---上次调度暂时挂起,只要重新调度就可以立即运行的---,和进入就绪的
    -- 睡眠时间到,等到资源,等待出错,等待超时,等等待已经完成的 --的参与调度。优先级高的先运行,界面线程优先级,可能获得一些动态提高;
    长期没有运行的,也可能获得,暂时的动态提高。当线程1的时间片到了,CPU也会强制让线程1终止啊,
    这种事情是不会出现的;
    时间片到了,系统会切换到调度程序,开始新的一轮线程调度,而不是强制线程退出。被切换的线程,要么下一轮仍然运行;
    要么让出CPU,以便别的线程运行,而自己会被系统挂起。强制退出,必须调用API函数。

    线程以下情况退出:
    1)抛出一个异常,没有被捕获
    2)双重异常,处理捕获异常时,又抛出异常。(这个地方只是个大概,具体情况记不大清楚了)
    3)被强制退出,其他位置调用TerminateThread。
    4)线程自己调用API----比如ExitThread 等函数;
    以及 _endthread,_endthreadex等库函数,会间接调用ExitThread等API 退出。
    5)线程自然结束--线程函数返回。一般我们都希望,线程自然结束,这样的代码不会出一点问题。否则,会有一大堆,让人头痛的问题要处理。
     
      

  26.   

    ReleaseMutex 实际作用是减少引用计数。