多个线程循环运行时如何影响索引

已感染

我试图编写一个运行5个线程并相应打印其索引的程序。

下面是代码:

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

int nthreads=5;

void *busy(void* c) {

    int my_busy = *(int *) c;

    printf("Hello World with thread index %d\n", my_busy);

    return NULL;
}    

int main()
{
    pthread_t t1[nthreads];
    void* result;

    for(int i=0; i<nthreads; i++)
    {
        pthread_create(&t1[i], NULL, busy, &i);
    }

    for(int i=0; i<nthreads; i++)
    {
        pthread_join(t1[i], &result);
    }
    return 0;
}

获得的输出:

Hello World with thread index 1
Hello World with thread index 4
Hello World with thread index 2
Hello World with thread index 0
Hello World with thread index 0

尽管所有5个线程都运行,为什么相应的索引没有正确输出?为什么我倾向于丢失一些索引,却又两次获取其他索引?例如,在这种情况下,我输了3,两次输出了0。尽管在一个循环中使用pthread_joinwithpthread_create可以解决此问题,但它不会安排所有线程并行运行。在这种情况下,应该怎么做才能打印所有索引?

名义动物

尽管所有5个线程都运行,为什么相应的索引没有正确输出?

您向每个线程传递一个指向变量的指针,并在线程函数访问它的同时修改该变量。您为什么期望线程函数看到任何特定值?它们同时运行在某些体系结构上,如果一个线程正在读取值而另一个线程正在修改值,则线程可能会看到一个完全不可能的乱码值。

例如,在这种情况下,我输了3,两次输出了0。

尽管由例如GCC生成的机器代码会在创建每个线程函数后增加由线程函数访问的变量,但由于未使用任何屏障或同步,因此在某些体系结构上线程函数观察到的值可能会“过时”。

这是否发生在您的特定计算机上(没有显式的障碍或同步),取决于您的计算机实现的内存排序模型


例如,在x86-64(又名AMD64; 64位Intel / AMD架构)上,将按顺序观察所有读取和写入,但可以在装入后对存储进行排序这意味着,如果最初说i = 0;和线程A一样i = 1;i == 0即使在线程A修改了变量之后,线程B仍然可以看到

请注意,添加的障碍(如_mm_fence()使用所提供的x86 / AMD64内在<immintrin.h>使用大多数C编译器时)是不够的,以确保每个线程看到一个独特的价值,因为每个线程的开始可以相对于当被延迟到真实世界的时刻pthread_create()是叫。他们所确保的只是至多一个线程看到零值。两个线程可能会看到值1,三个值会看到2,依此类推;甚至所有线程都可能看到值5。

在这种情况下,应该怎么做才能打印所有索引?

最简单的选择是提供要打印的索引作为值,而不是作为变量的指针。在busy()中,使用

my_busy = (int)(intptr_t)c;

在main()中,

pthread_create(&t1[i], NULL, busy, (void *)(intptr_t)i);

intptr_t类型是一种能够保存指针的带符号整数类型,并在中定义<stdint.h>(通常通过包含<inttypes.h>包含在内)。

(因为问题是标签,我也许应该指出的是,在Linux中,在所有的架构,你可以使用long,而不是intptr_t和,unsigned long而不是uintptr_t,我们在没有陷阱陈述或者long或者unsigned long,和每一个可能的long/unsigned long值可以转换成唯一的void *,反之亦然;往返保证正常工作。内核syscall接口要求这样做,因此将来也不太可能更改。)


如果需要将指针传递给i,但希望每个线程看到唯一的值,则需要使用某种同步。

最简单的同步方法是使用信号量。您可以将其设置为全局,但是使用一种结构来描述工作参数,并传递该结构的指针(即使所有工作线程都使用相同的指针)也更可靠:

#include <stdlib.h>
#include <pthread.h>
#include <semaphore.h>
#include <string.h>
#include <stdio.h>

#define  NTHREADS  5

struct work {
    int     i;
    sem_t   s;
};

void *worker(void *data)
{
    struct work *const  w = data;
    int                 i;

    /* Obtain a copy of the value. */
    i = w->i;

    /* Let others know we have copied the value. */
    sem_post(&w->s);

    /* Do the work. */
    printf("i == %d\n", i);
    fflush(stdout);

    return NULL;
}    

int main()
{
    pthread_t    thread[NTHREADS];
    struct work  w;
    int          rc, i;

    /* Initialize the semaphore. */
    sem_init(&w.s, 0, 0);

    /* Create the threads. */
    for (i = 0; i < NTHREADS; i++) {

        /* Create the thread. */
        w.i = i;
        rc = pthread_create(&thread[i], NULL, worker, &w);
        if (rc) {
            fprintf(stderr, "Failed to create thread %d: %s.\n", i, strerror(rc));
            exit(EXIT_FAILURE);
        }

        /* Wait for the thread function to grab its copy. */
        sem_wait(&w.s);
    }

    /* Reap the threads. */
    for (i = 0; i < NTHREADS; i++) {
        pthread_join(thread[i], NULL);
    }

    /* Done. */
    return EXIT_SUCCESS;
}

因为主线程(修改每个工作线程看到的值的线程)参与了同步,所以每个工作函数在创建下一个线程之前先读取该值,输出将始终以的顺序递增i


更好的方法是创建一个工作池,在该工作池中,主线程定义了要由线程共同完成的工作,而线程功能则以任意顺序简单地获取了下一个要完成的工作块:

#define  _POSIX_C_SOURCE  200809L
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
#include <limits.h>
#include <stdio.h>
#include <errno.h>

#define  NTHREADS  5
#define  LOOPS     3

struct work {
    pthread_mutex_t  lock;
    int              i;
};

void *worker(void *data)
{
    struct work *const  w = data;
    int                 n, i;

    for (n = 0; n < LOOPS; n++) {

        /* Grab next piece of work. */
        pthread_mutex_lock(&w->lock);
        i = w->i;
        w->i++;
        pthread_mutex_unlock(&w->lock);

        /* Display the work */
        printf("i == %d, n == %d\n", i, n);
        fflush(stdout);
    }

    return NULL;
}

int main(void)
{
    pthread_attr_t  attrs;
    pthread_t       thread[NTHREADS];
    struct work     w;
    int             i, rc;

    /* Create the work set. */
    pthread_mutex_init(&w.lock, NULL);
    w.i = 0;

    /* Thread workers don't need a lot of stack. */
    pthread_attr_init(&attrs);
    pthread_attr_setstacksize(&attrs, 2 * PTHREAD_STACK_MIN);

    /* Create the threads. */
    for (i = 0; i < NTHREADS; i++) {
        rc = pthread_create(thread + i, &attrs, worker, &w);
        if (rc != 0) {
            fprintf(stderr, "Error creating thread %d of %d: %s.\n", i + 1, NTHREADS, strerror(rc));
            exit(EXIT_FAILURE);
        }
    }

    /* The thread attribute set is no longer needed. */
    pthread_attr_destroy(&attrs);

    /* Reap the threads. */
    for (i = 0; i < NTHREADS; i++) {
        pthread_join(thread[i], NULL);
    }

    /* All done. */
    return EXIT_SUCCESS;
}

如果编译并运行此最后一个示例,您会注意到输出可能是奇数顺序的,但每个输出i都是唯一的,并且每次n = 0通过都n = LOOPS-1恰好发生了NTHREADS时间。

本文收集自互联网,转载请注明来源。

如有侵权,请联系 [email protected] 删除。

编辑于
0

我来说两句

0 条评论
登录 后参与评论

相关文章

python索引如何影响o-notation的运行时?

你如何找到相互影响的循环的运行时间?

多个“需要”会影响生产运行时间吗?

循环运行时如何获取链接?

如何将一个for循环划分为几个循环并使用线程运行它们以缩短运行时间?

@FunctionalInterface如何影响JVM的运行时行为?

运行时错误'91'...试图索引多个列

仅当线程当前未在 Python 中运行时才在循环中运行线程

PostgreSQL 索引运行时

元组的运行时索引

如何计算多线程程序的运行时间?

当服务在单独的线程上运行时,如何显示吐司?

如何在运行时给数组索引命名的键

写入文件的运行时影响

为什么循环顺序会影响矩阵乘法的运行时间?

运行时不可分循环大小对 openMP SIMD 的影响

如何解决for循环中的Java运行时错误

tkintermessagebox 在循环内运行时如何关闭它?

嵌套的“for”循环的运行时间是如何计算的?

如何得出循环的log(n)运行时间?

在一个解决方案中使用多个.NET运行时对性能/内存的影响

JavaFx:如何验证在运行时创建的多个 TextFields?

如何正确处理多个运行时错误?

如何在运行时销毁多个gameObject?

如何在运行时创建多个列表?

Indexeddb:在运行时之前不知道索引名称时,从多个索引获取数据

如何限制线程的执行时间并在线程运行时间过长时终止该线程?

Go运行时使用的线程数

运行时错误:主线程不在主循环中:乌龟图形和python