2012-05-05 6 views
5

Вопрос: Возможно ли гарантировать выполнение кода только в одном потоке за раз в многопоточной программе? (Или то, что приближает это)Выполнение серийного кода в многопоточной программе в C++

В частности: У меня есть контроллер M (который является резьба) и резьба A, B, C. Я хотел M, чтобы иметь возможность решила, кто должен иметь возможность работать. Когда нить закончила (окончательно или временно), управление переходит обратно на М.

Почему: В идеале я хочу, чтобы A, B и C выполняли свой код в своем потоке, а другие не работали. Это позволит каждому потоку сохранять указатель и стек инструкций, пока они останавливаются, начиная с того места, где они остановились, когда контроллер возвращает их обратно.

Что я сейчас делаю: Я написал код, который действительно может это сделать, но мне это не нравится.

В псевдо-C:

//Controller M 
//do some stuff 
UnlockMutex(mutex); 
do{}while(lockval==0); 
LockMutex(mutex); 
//continue with other stuff 


//Thread A 
//The controller currently has the mutex - will release it at UnlockMutex 
LockMutex(mutex); 
lockval=1; 
//do stuff 
UnlockMutex(mutex); 

Причина

do{}while(lockval==0); 

требуется в том, что, когда мьютекс разблокируется, А и М будет продолжаться. Этот хак гарантирует, что A не разблокирует мьютекс, прежде чем M сможет снова заблокировать его, чтобы A снова затормозил замок и снова запустил (он должен запускаться только один раз).

До-пока, похоже, перебор, но выполняет эту работу. Так что мой вопрос: есть лучший способ?

+0

Почему вы не хотите, чтобы потоки A, B и C работали одновременно? В чем проблема, которую вы пытаетесь решить? Существует почти наверняка лучший способ решить проблему, но вы должны сказать нам, что проблема * *. –

+0

Эта часть меня смущает: «Это позволит каждому потоку сохранять указатель и стек инструкций при паузе». Это не цель мьютексов.Операционная система (предполагающая модель превентивного потока) поддерживает стек каждого потока и регистрирует его, поскольку он дает им процессорное время. – veefu

+1

@veefu - OP хочет «зеленые нити», волокна AKA, (я думаю). A, B и C все получают свой собственный стек, но только один может фактически запускаться за раз, потому что есть только один поток ядра. –

ответ

2

Предполагая, что вы работаете в Windows, попробуйте взглянуть на волокна. (См. Например, http://developer.amd.com/Pages/1031200677.aspx или просто «стекловолокно» Google.)

Я подозреваю, что вы действительно ищете сопрограммы.

+0

Да - звучит как «резьба» AKA пользователя. –

+0

Это похоже на то, что мне нужно. Благодаря! – EggplantBonanza

+1

Если нити A, B и C все равно возвращаются к потоку контроллера M, почему бы просто не покончить с нитями все вместе и просто вызвать funcA fucnB и funcC из потока M? Действительно ли абстракция волокна необходима? – veefu

1

Проверьте наличие «CriticalSection» в Win32. В C++ 11 используется другой термин «lock_guard».

How do I make a critical section with Boost?

http://en.cppreference.com/w/cpp/thread/lock_guard

Ваш код

do{}while(lockval==0); 

съест вашу производительность процессора.

+0

Не говоря о том, что компилятор может оптимизировать его таким образом, чтобы он не видел изменения в 'lockval' из другого потока. –

0

Я предполагаю, что вы кодируете C++ под linux и используете pthread API. Вот код, не очень надежный, но хороший момент для начала. Надеюсь, полезно для вас. Использование «g ++ test_controller_thread.cpp -pthread -o test_controller_thread», чтобы сделать двоичного исполнительного директора.

// 3 threads, one for controller, the other two for worker1 and worker2. 
// Only one thread can proceed at any time. 
// We use one pthread_mutex_t and two pthread_cond_t to guarantee this. 
#include <pthread.h> 
#include <unistd.h> 
#include <stdio.h> 

static pthread_mutex_t g_mutex = PTHREAD_MUTEX_INITIALIZER; 
static pthread_cond_t g_controller_cond = PTHREAD_COND_INITIALIZER; 
static pthread_cond_t g_worker_cond = PTHREAD_COND_INITIALIZER; 

void* controller_func(void *arg) { 
    printf("entering the controller thread. \n"); 
    // limit the max time the controller can run 
    int max_run_time = 5; 
    int run_time = 0; 
    pthread_mutex_lock(&g_mutex); 
    while (run_time++ < max_run_time) { 
     printf("controller is waitting.\n"); 
     pthread_cond_wait(&g_controller_cond, &g_mutex); 
     printf("controller is woken up.\n"); 
     pthread_cond_signal(&g_worker_cond); 
     printf("signal worker to wake up.\n"); 
    } 
    pthread_mutex_unlock(&g_mutex); 
} 

void* worker_func(void *arg) { 
    int work_id = *(int*)arg; 
    printf("worker %d start.\n", work_id); 
    pthread_mutex_lock(&g_mutex); 
    while (1) { 
     printf("worker %d is waitting for controller.\n", work_id); 
     pthread_cond_wait(&g_worker_cond, &g_mutex); 
     printf("worker %d is working.\n", work_id); 
     pthread_cond_signal(&g_controller_cond); 
     printf("worker %d signal the controller.\n", work_id); 
    } 
    pthread_mutex_unlock(&g_mutex); 
} 

int main() { 
    pthread_t controller_thread, worker_thread_1, worker_thread_2; 
    int worker_id_1 = 1; 
    int worker_id_2 = 2; 
    pthread_create(&controller_thread, NULL, controller_func, NULL); 
    pthread_create(&worker_thread_1, NULL, worker_func, &worker_id_1); 
    pthread_create(&worker_thread_2, NULL, worker_func, &worker_id_2); 

    sleep(1); 
    printf("\nsignal the controller to start all the process.\n\n"); 
    pthread_cond_signal(&g_controller_cond); 

    pthread_join(controller_thread, NULL); 
    pthread_cancel(worker_thread_1); 
    pthread_cancel(worker_thread_2); 

    return 0; 
} 
Смежные вопросы