如何处理赛车线程(C ++; TinyThread ++)?

时间:2012-11-10 18:37:22

标签: c++ multithreading locking race-condition

以下是主要代码的示例(“Library / stack.h”并不重要,但无论如何,它是this previous question of mine中包含的最后一个来源):

#include <stdlib.h>
#include <time.h>

#include <iostream>
#include <tinythread.h>

#include "Library/stack.h"

using namespace std;
using namespace tthread;

#define BOULDERspd 100

// ========================================================================= //

struct Coord {
    int x, y;
};

int randOneIn (float n) {
    return ((int) (n * (rand() / (RAND_MAX + 1.0))));
}
int randOneIn (int n) {
    return ((int) ((float) n * (rand() / (RAND_MAX + 1.0))));
}

// ========================================================================= //

#include <windows.h>
void gotoxy (int column, int line) {
    if ((column >= 0) && (line >= 0)) {
        COORD coord;
        coord.X = column;
        coord.Y = line;
        SetConsoleCursorPosition(
            GetStdHandle( STD_OUTPUT_HANDLE ),
            coord
        );
    }
}

void gotoxy (Coord pos) {
    gotoxy(pos.x, pos.y);
}

// ========================================================================= //

void render (char image, Coord pos) {
    gotoxy(pos);
    cout << image;
}

void unrender (Coord pos) {
    gotoxy(pos);
    cout << ' ';
}

// ========================================================================= //

char randimage (void) {
    return (rand() % 132) + 123;
}

mutex xylock;

class Boulder {
    char avatar;
    Coord pos;

    public:
        Boulder (int inix) {
            pos.x = inix;
            pos.y = 0;

            avatar = randimage();
        };

        void fall (void) {

            unrender(pos);
            pos.y++;
            render(avatar, pos);

            Sleep(BOULDERspd);
        };

        void live (void) {
            do {
                fall();
            } while (y() < 20);
            die();
        };

        void die (void) {
            unrender(pos);
            pos.y = 0;
        };

        int x (void) { return pos.x; };
        int y (void) { return pos.y; };
};

// ========================================================================= //

class thrStack: public Stack<thread*> {
    public:
        thrStack (): Stack<thread*> () { };

        void pushNrun (thread* elem) {
            push(elem);
            top->core->joinable();
        }
};

void randBoulder (void* arg) {
    srand(time(NULL));
    Boulder boulder(rand() % 40);

    boulder.live();
}

void Boulders (void* arg) {
    srand(time(NULL));
    thrStack stack;

    do {
        stack.pushNrun(new thread (randBoulder, 0));
        Sleep(rand() % 300);
    } while(1);
}

// ========================================================================= //
// ========================================================================= //

int main() {
    thread raining (Boulders, 0);

    raining.join();
}

我是多线程的新手,所以,为了摆弄它,我正在尝试制作一个程序,使随机字符不断从屏幕顶部掉落,好像它正在下雨的ASCII符号。

然而,我注意到我的编码中有一点(大)错误:

bool xylock = false;

class Boulder {
    char avatar;
    Coord pos;

    public:
        Boulder (int inix) {
            pos.x = inix;
            pos.y = 0;

            avatar = randimage();
        };

        void fall (void) {

            unrender(pos);
            pos.y++;
            render(avatar, pos);

            Sleep(BOULDERspd);
        };

        void live (void) {
            do {
                fall();
            } while (y() < 20);
            die();
        };

        void die (void) {
            unrender(pos);
            pos.y = 0;
        };

        int x (void) { return pos.x; };
        int y (void) { return pos.y; };
};

因为fall()函数使用gotoxy来改变'全局游标',所以多次调用gotoxy会搞乱程序的预期执行。如果你试图按原样编译代码,你就会得到掉落的字母,不断切换位置并留下自己的垃圾。

有没有办法只使用TinyThread为这个和未来的情况使用或实现锁定?一般来说,在C ++中实现锁的逻辑是什么?


编辑:修改了fall();没关系,Caribou?

        void fall (void) {
            lock_guard<mutex> guard(xylock);

            unrender(pos);
            pos.y++;
            render(avatar, pos);

            xylock.unlock();

            Sleep(BOULDERspd);
        };

2 个答案:

答案 0 :(得分:1)

您可以使用tinythread lib:

http://tinythreadpp.bitsnbites.eu/doc/

具体查看lock_guardmutex

  

对gotoxy的多次调用会搞乱预期的执行   程序。如果你试图按原样编译代码,你就会堕落   不断切换位置并留下垃圾的字母   他们自己身后。

创建一个mutex对象进行同步,然后在您希望线程安全的函数中使用它创建一个本地lock_guard。此mutex也可以使用lock_guard

在多个地方使用

答案 1 :(得分:1)

这里我创建了一个没有框架或类的非常基本的线程示例。正如您所看到的,线程化和同步化不是C ++的工作,而是OS工作! ; - )

这里我创建了一个简单的线程函数,我调用了两次。线程写入相同的变量,但不能同时执行,因此必须保护它。在此示例中,我使用CRITICAL_SECTION对象将变量锁定一个线程。如果一个线程锁定它,另一个线程无法访问它,必须等到它空闲。

仔细看看,我也保护了printf操作。如果你不这样做会怎么样?你会得到一个非常有趣的外包!找出原因并了解线程和锁的工作原理。 :-)

#include <windows.h>
#include <stdlib.h>
#include <string>
#include <iostream>
#include <process.h>

//a global variable (just do do someting):
int g_ThreadCounter = 0;


//global variable to end the threads:
bool g_Run = true;
//do not use global variables, there are better solutions! I just did it here to 
//keep it simple and focus on the issue!

//a critical section object - something like a "soft-version" of a mutex to synchronize
//write access on variables
CRITICAL_SECTION critical;
//a thread function
unsigned  __stdcall threadFunc(void *pThreadNum)
{

    unsigned int iThreadNum = reinterpret_cast<unsigned int>(pThreadNum);

    do{
    //you need the critical section only when you change values:
    EnterCriticalSection(&critical);
        g_ThreadCounter++;

        printf("from thread: ");
        printf("%d", iThreadNum);
        printf(" counter = ");      
        printf("%d", g_ThreadCounter);
        printf("\n");
    LeaveCriticalSection(&critical);

    //sleep a secound
    Sleep (1000);
}while(g_Run);

_endthreadex(0);
return 0;
}

int main()
{
unsigned int ThreadID1 = 1;

unsigned int ThreadID2 = 2;
//initialize the critical section with spin count (can be very effective in case
//of short operation times, see msdn for more information)
if(!InitializeCriticalSectionAndSpinCount(&critical, 1000))
{
    //DO NOT START THE THREADS, YOU DON'T HAVE SYNCHRONISATION!!!
    printf("someting went wrong, press any key to exit");
    //do some error handling
    getchar();
    exit(-1);
}

//start the threads
HANDLE thHandle1 = (HANDLE)_beginthreadex(NULL, 0, &threadFunc, (void*) ThreadID1, 0, NULL);

HANDLE thHandle2 = (HANDLE)_beginthreadex(NULL, 0, &threadFunc, (void*) ThreadID2, 0, NULL);

if(thHandle1 == INVALID_HANDLE_VALUE || thHandle2 == INVALID_HANDLE_VALUE)
{
    printf("something went wrong, press any key to exit");
    //do some error handling
    getchar();
    exit(-1);
}
//the main thread sleeps while the other threads are working
Sleep(5000);

//set the stop variable
EnterCriticalSection(&critical);
    g_Run = false;
LeaveCriticalSection(&critical);

//wait for the thread; infinite means, you wait as long as the 
    //thread takes to finish
WaitForSingleObject(thHandle1, INFINITE);
CloseHandle(thHandle1);

WaitForSingleObject(thHandle2, INFINITE);
CloseHandle(thHandle2);

DeleteCriticalSection(&critical);

printf("press any key to exit");
getchar();

return 0;
}

研究您正在使用的操作系统!它有时比对框架和外国课程过分关注更好。这可以解决很多问题!