0

这是主要代码的示例(“Library/stack.h”并不重要,但无论如何,它是我的上一个问题中包含的最后一个来源):

#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(); 没事吧,驯鹿?

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

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

            xylock.unlock();

            Sleep(BOULDERspd);
        };
4

2 回答 2

1

您可以使用 tinythread 库:

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

具体看lock_guardmutex

多次调用 gotoxy 会打乱程序的预期执行。如果您尝试按原样编译代码,您会得到不断切换位置并留下垃圾的掉落字母。

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

于 2012-11-10T18:42:20.973 回答
1

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

这里我创建了一个简单的线程函数,我调用了两次。线程写入相同的变量,但不能同时执行,所以必须保护它。在此示例中,我使用 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;
}

研究您正在使用的操作系统!有时总比过分关注框架和外来类要好。这可以解决很多问题!

于 2012-11-10T21:32:47.583 回答