gpt4 book ai didi

c - POSIX 线程,独特的执行

转载 作者:行者123 更新时间:2023-11-30 18:49:25 27 4
gpt4 key购买 nike

有没有办法确保只有 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 知道新的 rand 值。或 broadcasting .

此外,线程必须都结束了他们的工作 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 ,然后将其强制为 void * ;被调用函数撤消此序列

每个线程的功能并不复杂。它从其参数中收集线程号,然后进入一个循环以迭代所需的循环数。在每次迭代中,它将迭代号(和线程号)传递给 next_iteration_random_number() ,它协调随机数的生成。线程打印数据,然后使用 nanosleep() sleep 时间少于 1 秒,不易辨认。

有趣的代码在 next_iteration_random_number() .首先,线程锁定控制共享信息的互斥锁。然后它增加等待线程的数量。如果当前循环与其正在处理的迭代不同,并且等待线程数不等于线程总数,则该线程调用 pthread_cond_wait() sleep 直到广播到。当线程退出循环时(通过在广播后唤醒,或者因为它从未进入循环),线程查看当前循环是否是它所期望的迭代。如果不是,它必须是这个循环中尝试的最后一个线程——所以它会生成随机数并进行内务处理以确保其他线程知道发生了什么,
然后它使用 pthread_cond_broadcast()通知所有 sleep 线程唤醒。他们实际上还不能醒来;当前线程仍然持有互斥锁。它报告它正在做什么并解锁互斥锁,并返回随机数。其他线程检测到循环编号与它们等待的迭代相同,因此它们可以各自打印信息、解锁互斥体并返回编号。

关键点是每个线程必须知道它期望在哪个迭代上工作,并且当那还不是当前迭代时必须适本地休眠。代码中有大量的断言和打印操作来帮助理解发生了什么。

完全分开,因为 lrand48()返回 long (尽管值在 [0,231 范围内),因此即使 long 时它也是 32 位值是 64 位类型),这意味着它很有可能返回无法在 float 中准确表示的值,所以 gl_rand = (float)lrand48();从问题中截取的代码是可疑的。 (示例代码还将 long 值记录在 gl_long 中,并且偶尔会使用它。)

该代码在运行 macOS Sierra 10.12.3 且 GCC 6.3.0 且编译选项设置为 fussy 的 Mac 上干净地编译:
$ gcc -O3 -g -I../../inc -std=c11 -Wall -Wextra -Werror -Wmissing-prototypes \
> -Wstrict-prototypes -Wold-style-definition pthrd37.c -o pthrd37 \
> -L../../lib -lsoq
$

sample 运行

程序配置为 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;
}

关于c - POSIX 线程,独特的执行,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/42822480/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com