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();
}

私はマルチスレッドに慣れていないので、それをいじるために、ランダムな文字が画面の上部から常に降ってくるようにするプログラムを作成しようとしています。

ただし、コーディングに小さな (大きな) エラーがあることに気付きました。

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++ で実装されているロックのロジックは何ですか?


編集:変更された秋(); 大丈夫か、カリブー?

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

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

            xylock.unlock();

            Sleep(BOULDERspd);
        };
4

2 に答える 2

1

ここでは、フレームワークやクラスを使用しない非常に基本的なスレッドの例を作成しました。ご覧のとおり、スレッド化と同期化は C++ の作業ではなく、OS の作業です! ;-)

ここでは、2 回呼び出す簡単なスレッド関数を作成しました。スレッドは同じ変数を書き込んでいますが、同時にそれを行うことはできないため、保護する必要があります。このサンプルでは、​​CRITICAL_SECTION オブジェクトを使用して、変数を 1 つのスレッドでロックします。1 つのスレッドがロックすると、もう 1 つのスレッドはアクセスできず、解放されるまで待たなければなりません。

よく見ると、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;
}

あなたが働いているOSを勉強してください!フレームワークや外部クラスに注意を払いすぎるよりも良い場合があります。これでいろいろな疑問が解決できます!

于 2012-11-10T21:32:47.583 に答える
1

tinythread ライブラリを使用できます。

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

具体的に見てlock_guardmutex

gotoxy を複数回呼び出すと、意図したプログラムの実行が台無しになります。コードをそのままコンパイルしようとすると、常に位置が入れ替わってゴミが残る落下文字が発生します。

同期するオブジェクトを作成し、mutexスレッドセーフにしたい関数でそれをlock_guard使用してローカルを作成します。これmutexは、 を使用して複数の場所で使用することもできますlock_guard

于 2012-11-10T18:42:20.973 に答える