[英]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 variable
或broadcasting
了解新的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_t
為void *
; 被調用函數撤消該序列
每線程功能並不復雜。 它從其參數中收集線程號,然后進入循環以迭代所需的循環數。 在每次迭代中,它將迭代編號(和線程編號)傳遞給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.