首页 新闻 赞助 找找看

多线程两线程用临界区切换输出数字

0
悬赏园豆:20 [已解决问题] 解决于 2015-11-06 10:50

输出tickets每次减1的值,用线程1,线程2切换输出,并且使用了临界区来限制线程对于tickets的访问,但是输出的时候线程1没有办法进入tickets的使用权

#include<stdio.h>
#include<process.h>
#include<windows.h>
int tickets=50;
CRITICAL_SECTION g_cs;

unsigned int __stdcall Fun1(VOID *lp)
{
while(true)
{
EnterCriticalSection(&g_cs);
Sleep(1);
if(tickets>0)
{
printf("thread1 %d\n",tickets--);
LeaveCriticalSection(&g_cs);
}
else
{
LeaveCriticalSection(&g_cs);
break;
}
}
}

unsigned int __stdcall Fun2(VOID *lp)
{
while(true)
{
EnterCriticalSection(&g_cs);
Sleep(1);
if(tickets>0)
{
printf("thread1 %d\n",tickets--);
LeaveCriticalSection(&g_cs);
}
else
{
LeaveCriticalSection(&g_cs);
break;
}
}
}

int main()
{
HANDLE handle1,handle2;
handle1=(HANDLE)_beginthreadex(NULL,0,Fun1,NULL,0,NULL);
handle2=(HANDLE)_beginthreadex(NULL,0,Fun2,NULL,0,NULL);
CloseHandle(handle1);
CloseHandle(handle2);

InitializeCriticalSection(&g_cs);
Sleep(4000);
DeleteCriticalSection(&g_cs);
return 0;
}

输出

编译器用的codeblocks

calmound的主页 calmound | 初学一级 | 园豆:184
提问于:2014-09-18 21:08
< >
分享
最佳答案
0

你的程序,不是进不了线程2,而是很少能进入线程2(我实验的时候,偶尔能进入,有时开头,有时结尾,有时中间,反正很少),解决你的问题的办法:

把sleep(1)移动到锁的外面。

你的sleep在锁里面,这样,变成处理数据的时候要延时,从性能角度考虑不好。从逻辑考虑:

因为你解锁后,线程马上又进入循环,此时线程2可能还来不及获取锁已经解锁的状态就又被线程1再次锁定了。

519740105 | 大侠五级 |园豆:5810 | 2014-09-19 09:45

谢谢你,我想在问下为什么会出现已停止工作的情况。这段代码是按照vc++深入详解(孙鑫版)但是他的视频里头运行就是正常的是电脑原因?

calmound | 园豆:184 (初学一级) | 2014-09-19 09:48

借用 一楼 简化后的代码:

#include<stdio.h>
#include<process.h>
#include<windows.h>
int tickets=50;
CRITICAL_SECTION g_cs;

unsigned int __stdcall Fun(VOID *lp)
{
    char *cp = (char *)lp;
    while(true)    {
        Sleep(1);//很关键,不能放在锁里面,放在锁前锁后没关系,锁前是预处理,锁后是后处理。
        //如果锁里的内容是线程处理的全部,那么,这样的过程是不适合使用多线程的
        //而你的代码恰巧是处理的全部,从而导致锁的繁忙。
        EnterCriticalSection(&g_cs);
        if(tickets>0) {
            printf("%s %d\n",cp, tickets--);
            LeaveCriticalSection(&g_cs);
        } else {
            LeaveCriticalSection(&g_cs);
            break;
        }
    }
    return 0;
}

int main()
{
    HANDLE handle1,handle2;
    InitializeCriticalSection(&g_cs);

    handle1=(HANDLE)_beginthreadex(NULL,0,Fun,"Thread1 ",0,NULL);
    handle2=(HANDLE)_beginthreadex(NULL,0,Fun,"Thread2 ",0,NULL);
    
    WaitForSingleObject(handle1,INFINITE);
    CloseHandle(handle1);
    WaitForSingleObject(handle2,INFINITE);
    CloseHandle(handle2);

    DeleteCriticalSection(&g_cs);
    return 0;
}
519740105 | 园豆:5810 (大侠五级) | 2014-09-19 09:49

 很奇怪的是,我把代码类化,运行报告错误,错误点是加锁的时候,没细研究,如果你能解决,可以告知一下:

#pragma once
#include<stdio.h>
#include<process.h>
#include<windows.h>
#define _USERENTRY __stdcall
class ThreadTest
{
private:
    int tickets = 50;
    CRITICAL_SECTION g_cs;
private:
    unsigned int __stdcall Fun(VOID *lp);
public:
    ThreadTest();
    ~ThreadTest();
public:
    void Test();
};

 

#include "ThreadTest.h"


ThreadTest::ThreadTest()
{
}


ThreadTest::~ThreadTest()
{
}

void ThreadTest::Test()
{
    HANDLE handle1, handle2;
    InitializeCriticalSection(&g_cs);
    union { // 联合类,用于转换类成员方法指针到普通函数指针(试过编译器不允许在这两种函数之间强制转换),不知道有没有更好的方法。
        unsigned int (_USERENTRY *ThreadProc)(void *);
        unsigned int (_USERENTRY ThreadTest::*MemberProc)(void *);
    } Proc;
    Proc.MemberProc = &ThreadTest::Fun;

    handle1 = (HANDLE)_beginthreadex(NULL, 0, Proc.ThreadProc, "Thread1 ", 0, NULL);
    handle2 = (HANDLE)_beginthreadex(NULL, 0, Proc.ThreadProc, "Thread2 ", 0, NULL);

    WaitForSingleObject(handle1, INFINITE);
    CloseHandle(handle1);
    WaitForSingleObject(handle2, INFINITE);
    CloseHandle(handle2);

    DeleteCriticalSection(&g_cs);
}
unsigned int __stdcall ThreadTest::Fun(VOID *lp)
{
    char *cp = (char *)lp;
    while (true)    {
        EnterCriticalSection(&g_cs);
        if (tickets>0) {
            printf("%s %d\n", cp, tickets--);
            LeaveCriticalSection(&g_cs);
        }
        else {
            LeaveCriticalSection(&g_cs);
            break;
        }
        Sleep(1);
    }
    return 0;
}
519740105 | 园豆:5810 (大侠五级) | 2014-09-19 09:54

@calmound: 这个问题好还真的不知道,应该是锁的释放未完成导致的。

我在实验中遇到了异常:

1、中断了main函数

2、直接关闭了控制台窗口(应用窗口)

3、在VS里终止了调试

结果问题出来了:VS卡住了。后来是通过进程管理器把进程关闭,VS才能用起来,但是,进程管理器里的进程却一直关闭失败,现在还在。郁闷呢。

519740105 | 园豆:5810 (大侠五级) | 2014-09-19 09:57

@calmound: 我的实验,在我的电脑上是没报告你这个错误。

我想,你参考下一楼代码的修改,他把锁的初始化放在了前面,而你是在启动线程之后,把这个位置调整下看。

(把锁提前初始化,这个问题即便不受影响也应该有这个习惯)

如果跟这个问题没关系,那就可能是你的电脑或系统的问题了。

519740105 | 园豆:5810 (大侠五级) | 2014-09-19 10:05
其他回答(2)
0

InitializeCriticalSection(&g_cs);

handle1=(HANDLE)_beginthreadex(NULL,0,Fun1,NULL,0,NULL);
handle2=(HANDLE)_beginthreadex(NULL,0,Fun2,NULL,0,NULL);

Sleep(4000);

CloseHandle(handle1);
CloseHandle(handle2);

DeleteCriticalSection(&g_cs);

ayiis | 园豆:356 (菜鸟二级) | 2014-09-18 22:40
0

#include<stdio.h>
#include<process.h>
#include<windows.h>
int tickets=50;
CRITICAL_SECTION g_cs;

unsigned int __stdcall Fun(VOID *lp)
{
    char *cp = (char *)lp;
    while(true)    {
        EnterCriticalSection(&g_cs);
        Sleep(1);
        if(tickets>0) {
            printf("%s %d\n",cp, tickets--);
            LeaveCriticalSection(&g_cs);
        } else {
            LeaveCriticalSection(&g_cs);
            break;
        }
    }
    return 0;
}

int main()
{
    HANDLE handle1,handle2;
    InitializeCriticalSection(&g_cs);

    handle1=(HANDLE)_beginthreadex(NULL,0,Fun,"Thread1 ",0,NULL);
    handle2=(HANDLE)_beginthreadex(NULL,0,Fun,"Thread2 ",0,NULL);
    
    WaitForSingleObject(handle1,INFINITE);
    CloseHandle(handle1);
    WaitForSingleObject(handle2,INFINITE);
    CloseHandle(handle2);

    DeleteCriticalSection(&g_cs);
    return 0;
}

 

这样是不是会简洁很多呢

收获园豆:20
飞鸿眉敛 | 园豆:256 (菜鸟二级) | 2014-09-18 23:12

你好,我想问下我那个哪里错了?为什么无法运行

支持(0) 反对(0) calmound | 园豆:184 (初学一级) | 2014-09-19 09:04

@calmound: InitializeCriticalSection(&g_cs);你的g_cs没有初始化,这是很关键的。

其次,与你这个出错没有关系的一个问题是,不要等待线程运行结束不要用sleep,正确的是WaitForSingleObject

支持(0) 反对(0) 飞鸿眉敛 | 园豆:256 (菜鸟二级) | 2014-09-19 11:31

@calmound: 我也看了下你在三楼的另外的代码,很成问题

支持(0) 反对(0) 飞鸿眉敛 | 园豆:256 (菜鸟二级) | 2014-09-19 11:34

@calmound: 如果要设计成类,一般 InitializeCriticalSection(&g_cs);在类的构造函数,或者说初始化函数,DeleteCriticalSection(&g_cs);在类的析构函数,或者说摧毁函数。而线程的运行函数unsigned int __stdcall Fun(VOID *lp);应该设计成static函数,这样就与对象无关,可以用类名访问。看见你和三楼两个不懂多线程的讨论,呵呵

支持(0) 反对(0) 飞鸿眉敛 | 园豆:256 (菜鸟二级) | 2014-09-19 11:44
清除回答草稿
   您需要登录以后才能回答,未注册用户请先注册