簡體   English   中英

POSIX線程,唯一執行

[英]POSIX Threads, unique execution

有沒有辦法確保只有1個線程執行特定功能?

for (i = startnotfirst; i < end; i++) {     

    gl_rand = (float) lrand48();

    fprintf(stderr, "\nTHREADING - #ID: %d - THIS IS MY RAND: %f", *mytid,rand);
    to_open = (rand / (float) INT_MAX) < (points->p[i].cost / z);
    if (to_open) {
        //printf("\nRANDOM: %f \nINT_MAX: %d \npointsDistance(cost): %f \nZ: %f \n\n RESULT: %f < %f\n\n\n",rand ,INT_MAX,points->p[1].cost , z,(rand / (float) INT_MAX),(points->p[i].cost / z));
        fprintf(stderr, "\nTHREADING - #ID: %d - im working...", *mytid);
        t_kcenter++;
        for (int k = start; k < end; k++) {
            float distance = dist(points->p[i], points->p[k], points->dim);
            //If distance is smaller add it to that cluster.
            float new_cost = distance * points->p[k].weight;
            if (new_cost < points->p[k].cost) {
                points->p[k].cost = new_cost;
                points->p[k].assign = i;
            }
        }
    }
}

我希望gl_rand僅由一個線程執行,而其他線程則可以通過更改global variablebroadcasting了解新的rand值。

同樣,在新的數字出現之前,線程必須都已結束其工作iteration body

有任何想法嗎? 謝謝!

pthread_cond_wait()pthread_cond_broadcast()

pthread_mutex_lock() pthread_mutex_unlock()結合使用, pthread_cond_wait()pthread_cond_broadcast()函數是實現所需功能的關鍵。 但是,需要非常小心才能使謂詞正確。

/*
** Objective: N threads cooperate on M cycles or iterations of a task.
** A new random number is needed for each cycle, but all threads must
** use the same random number on each cycle.
** Any thread may evaluate the new random number.
*/

#include <assert.h>
#include <errno.h>
#include <pthread.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include "stderr.h"

#ifndef NUM_THREADS
#define NUM_THREADS 3
#endif
#ifndef NUM_CYCLES
#define NUM_CYCLES 5
#endif

enum { MAX_THREADS = NUM_THREADS };
enum { MAX_CYCLES  = NUM_CYCLES  };

static pthread_mutex_t mtx_waiting = PTHREAD_MUTEX_INITIALIZER;
static pthread_cond_t  cnd_waiting = PTHREAD_COND_INITIALIZER;
static int             num_waiting = 0;
static int             cycle   = -1;

static float gl_rand = 0;
static long  gl_long = 0;

static float next_iteration_random_number(int tid, int iteration)
{
    pthread_mutex_lock(&mtx_waiting);
    assert(cycle == iteration || cycle == iteration - 1);
    num_waiting++;
    printf("-->> TID %d, I = %d (C = %d, W = %d)\n",
           tid, iteration, cycle, num_waiting);
    while (cycle != iteration && num_waiting != MAX_THREADS)
    {
        assert(num_waiting > 0 && num_waiting <= MAX_THREADS);
        printf("-CW- TID %d, I = %d (C = %d, W = %d)\n",
               tid, iteration, cycle, num_waiting);
        pthread_cond_wait(&cnd_waiting, &mtx_waiting);
    }
    assert(cycle == iteration || num_waiting == MAX_THREADS);
    printf("---- TID %d, I = %d (C = %d, W = %d)\n",
           tid, iteration, cycle, num_waiting);

    if (cycle != iteration)
    {
        gl_long = lrand48();
        gl_rand = (float)gl_long;
        num_waiting = 0;
        cycle = iteration;
        printf("---- TID %d generates cycle %d: L = %ld, F = %g\n",
               tid, cycle, gl_long, gl_rand);
        pthread_cond_broadcast(&cnd_waiting);
    }

    printf("<<-- TID %d, I = %d (C = %d, W = %d) L = %ld, F = %g\n",
           tid, iteration, cycle, num_waiting, gl_long, gl_rand);
    pthread_mutex_unlock(&mtx_waiting);
    return gl_rand;
}

static void *thread_function(void *vp)
{
    int tid = (int)(uintptr_t)vp;     // Thuggish!

    for (int i = 0; i < MAX_CYCLES; i++)
    {
        float f = next_iteration_random_number(tid, i);
        printf("TID %d at work: I = %d, F = %g\n", tid, i, f);
        fflush(stdout);
        struct timespec rq;
        rq.tv_sec = 0;
        rq.tv_nsec = (((gl_long & 0xFF) + (0xF * tid))) % 200 * 50000000;
        assert(rq.tv_nsec >= 0 && rq.tv_nsec < 10000000000);
        nanosleep(&rq, 0);
    }

    return 0;
}

int main(int argc, char **argv)
{
    err_setarg0(argv[0]);
    assert(argc == 1);

    pthread_t thread[MAX_THREADS];

    for (int i = 0; i < MAX_THREADS; i++)
    {
        int rc = pthread_create(&thread[i], 0, thread_function, (void *)(uintptr_t)i);
        if (rc != 0)
        {
            errno = rc;
            err_syserr("failed to create thread %d", i);
        }
    }

    for (int i = 0; i < MAX_THREADS; i++)
    {
        void *vp;
        int rc = pthread_join(thread[i], &vp);
        if (rc != 0)
        {
            errno = rc;
            err_syserr("Failed to join TID %d", i);
        }
        printf("TID %d returned %p\n", i, vp);
    }

    return 0;
}

來源GitHub libsoq庫代碼。 在啟動例程err_在頭部聲明stderr.h和支撐代碼是在stderr.c 這些大大簡化了錯誤報告。

main()函數很簡單; 它啟動5個線程,然后等待加入5個線程。 唯一棘手的問題是將線程號作為參數傳遞給線程函數。 代碼將整數值轉換為uintptr_t ,然后將其uintptr_tvoid * 被調用函數撤消該序列

每線程功能並不復雜。 它從其參數中收集線程號,然后進入循環以迭代所需的循環數。 在每次迭代中,它將迭代編號(和線程編號)傳遞給next_iteration_random_number() ,后者協調隨機數的生成。 該線程將打印數據,然后使用nanosleep()進行不容易辨別的不到1秒的時間睡眠。

有趣的代碼在next_iteration_random_number() 首先,線程鎖定控制共享信息的互斥鎖。 然后,它增加了等待線程的數量。 如果當前周期與正在處理的迭代周期不同,並且等待線程的數量不等於線程總數,則此線程將調用pthread_cond_wait()進行睡眠,直到廣播到。 當線程退出循環時(通過廣播后喚醒,或者因為它從未進入循環),線程將查看當前循環是否為預期的迭代。 如果不是,則它必須是該周期要嘗試的最后一個線程-因此它將生成隨機數並進行內部管理,以確保其他線程將知道發生了什么,然后使用pthread_cond_broadcast()發出信號通知所有休眠線程喚醒。 他們實際上還不能醒來。 當前線程仍保留互斥體。 它報告正在執行的操作並解鎖互斥鎖,然后返回隨機數。 其他線程檢測到循環編號與它們等待的迭代相同,因此它們可以分別打印信息,解鎖互斥鎖並返回該編號。

關鍵是每個線程必須知道它預期要進行哪個迭代,並且在該線程不是當前迭代時必須適當休眠。 代碼中包含大量斷言和打印操作,可幫助您了解發生了什么。

完全分開,因為lrand48()返回一個long (盡管其值的范圍為[0,2 31 ),所以即使long是64位類型,它也是32位值),這意味着它有可能返回無法精確表示為float ,因此gl_rand = (float)lrand48(); 從問題中刪除的代碼是可疑的。 (示例代碼還在gl_long記錄了long值,並偶爾使用它。)

該代碼可以在運行帶有GCC 6.3.0的macOS Sierra 10.12.3的Mac上干凈地編譯,並將編譯選項設置為fussy:

$ gcc -O3 -g -I../../inc -std=c11 -Wall -Wextra -Werror -Wmissing-prototypes \
>     -Wstrict-prototypes -Wold-style-definition pthrd37.c -o pthrd37 \
>     -L../../lib -lsoq 
$

樣品運行

程序配置了3個線程和5個周期:

-->> TID 0, I = 0 (C = -1, W = 1)
-CW- TID 0, I = 0 (C = -1, W = 1)
-->> TID 2, I = 0 (C = -1, W = 2)
-CW- TID 2, I = 0 (C = -1, W = 2)
-->> TID 1, I = 0 (C = -1, W = 3)
---- TID 1, I = 0 (C = -1, W = 3)
---- TID 1 generates cycle 0: L = 851401618, F = 8.51402e+08
<<-- TID 1, I = 0 (C = 0, W = 0) L = 851401618, F = 8.51402e+08
TID 1 at work: I = 0, F = 8.51402e+08
---- TID 0, I = 0 (C = 0, W = 0)
<<-- TID 0, I = 0 (C = 0, W = 0) L = 851401618, F = 8.51402e+08
TID 0 at work: I = 0, F = 8.51402e+08
---- TID 2, I = 0 (C = 0, W = 0)
<<-- TID 2, I = 0 (C = 0, W = 0) L = 851401618, F = 8.51402e+08
TID 2 at work: I = 0, F = 8.51402e+08
-->> TID 1, I = 1 (C = 0, W = 1)
-CW- TID 1, I = 1 (C = 0, W = 1)
-->> TID 0, I = 1 (C = 0, W = 2)
-CW- TID 0, I = 1 (C = 0, W = 2)
-->> TID 2, I = 1 (C = 0, W = 3)
---- TID 2, I = 1 (C = 0, W = 3)
---- TID 2 generates cycle 1: L = 1804928587, F = 1.80493e+09
<<-- TID 2, I = 1 (C = 1, W = 0) L = 1804928587, F = 1.80493e+09
TID 2 at work: I = 1, F = 1.80493e+09
---- TID 1, I = 1 (C = 1, W = 0)
<<-- TID 1, I = 1 (C = 1, W = 0) L = 1804928587, F = 1.80493e+09
TID 1 at work: I = 1, F = 1.80493e+09
---- TID 0, I = 1 (C = 1, W = 0)
<<-- TID 0, I = 1 (C = 1, W = 0) L = 1804928587, F = 1.80493e+09
TID 0 at work: I = 1, F = 1.80493e+09
-->> TID 2, I = 2 (C = 1, W = 1)
-CW- TID 2, I = 2 (C = 1, W = 1)
-->> TID 1, I = 2 (C = 1, W = 2)
-CW- TID 1, I = 2 (C = 1, W = 2)
-->> TID 0, I = 2 (C = 1, W = 3)
---- TID 0, I = 2 (C = 1, W = 3)
---- TID 0 generates cycle 2: L = 758783491, F = 7.58783e+08
<<-- TID 0, I = 2 (C = 2, W = 0) L = 758783491, F = 7.58783e+08
TID 0 at work: I = 2, F = 7.58783e+08
---- TID 2, I = 2 (C = 2, W = 0)
<<-- TID 2, I = 2 (C = 2, W = 0) L = 758783491, F = 7.58783e+08
TID 2 at work: I = 2, F = 7.58783e+08
---- TID 1, I = 2 (C = 2, W = 0)
<<-- TID 1, I = 2 (C = 2, W = 0) L = 758783491, F = 7.58783e+08
TID 1 at work: I = 2, F = 7.58783e+08
-->> TID 0, I = 3 (C = 2, W = 1)
-CW- TID 0, I = 3 (C = 2, W = 1)
-->> TID 2, I = 3 (C = 2, W = 2)
-CW- TID 2, I = 3 (C = 2, W = 2)
-->> TID 1, I = 3 (C = 2, W = 3)
---- TID 1, I = 3 (C = 2, W = 3)
---- TID 1 generates cycle 3: L = 959030623, F = 9.59031e+08
<<-- TID 1, I = 3 (C = 3, W = 0) L = 959030623, F = 9.59031e+08
TID 1 at work: I = 3, F = 9.59031e+08
-->> TID 1, I = 4 (C = 3, W = 1)
-CW- TID 1, I = 4 (C = 3, W = 1)
---- TID 0, I = 3 (C = 3, W = 1)
<<-- TID 0, I = 3 (C = 3, W = 1) L = 959030623, F = 9.59031e+08
TID 0 at work: I = 3, F = 9.59031e+08
---- TID 2, I = 3 (C = 3, W = 1)
<<-- TID 2, I = 3 (C = 3, W = 1) L = 959030623, F = 9.59031e+08
TID 2 at work: I = 3, F = 9.59031e+08
-->> TID 0, I = 4 (C = 3, W = 2)
-CW- TID 0, I = 4 (C = 3, W = 2)
-->> TID 2, I = 4 (C = 3, W = 3)
---- TID 2, I = 4 (C = 3, W = 3)
---- TID 2 generates cycle 4: L = 684387517, F = 6.84388e+08
<<-- TID 2, I = 4 (C = 4, W = 0) L = 684387517, F = 6.84388e+08
TID 2 at work: I = 4, F = 6.84388e+08
---- TID 1, I = 4 (C = 4, W = 0)
<<-- TID 1, I = 4 (C = 4, W = 0) L = 684387517, F = 6.84388e+08
TID 1 at work: I = 4, F = 6.84388e+08
---- TID 0, I = 4 (C = 4, W = 0)
<<-- TID 0, I = 4 (C = 4, W = 0) L = 684387517, F = 6.84388e+08
TID 0 at work: I = 4, F = 6.84388e+08
TID 0 returned 0x0
TID 1 returned 0x0
TID 2 returned 0x0

請注意,每個線程碰巧都會至少產生一次隨機數。 那不是可以保證的,但是它表明沒有一個線程享有特權。

pthread_once()

此答案的第一個版本提到並說明了pthread_once()調用。 這並不是實際需要的,但是在其他情況下可能很有用。

假設您有一個與POSIX完全兼容的系統,如果您只希望一個線程執行某項操作,但是不要緊是由哪個線程執行,那么我相信您正在尋找pthread_once()

#include <pthread.h>
#include <stdlib.h>

static pthread_once_t once_only = PTHREAD_ONCE_INIT;

static float gl_rand = 0;

static void pt_once(void)
{
    gl_rand = (float)lrand48();
}

void *thread_function(void *vp)
{
     pthread_once(&once_only, pt_once);
     …rest of the code…
     return 0;
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM