【问题标题】:C - Pthreads threads go into infinite loop unless I print the output in betweenC - Pthreads线程进入无限循环,除非我在两者之间打印输出
【发布时间】:2021-07-03 18:23:02
【问题描述】:
#include <pthread.h>
#include <stdio.h>

#define WIDTH   1000
#define HEIGHT  1000

typedef struct  s_pair
{
    int x;
    int y;
}               t_pair;

char    pixels[HEIGHT][WIDTH];

void    *add_to_global(void *param)
{
    t_pair  *input;

    input = (t_pair *)param;
    if (!(input->x % 2))
        pixels[input->y][input->x] = '.';
    else if (!(input->x % 3))
        pixels[input->y][input->x] = '#';
    else
        pixels[input->y][input->x] = ' ';
    return (NULL);
}

void    thread_runner(int x_start, int x_end, int y_start, int y_end)
{
    pthread_t   workers[10];
    t_pair      inputs[10];
    int         i, x, y;

    y = y_start - 1;
    while (++y < y_end)
    {
        x = x_start - 1;
        i = -1;
        while (x < x_end)
        {
            if (++i < 10)
            {
                inputs[i] = (t_pair){++x, y};
                pthread_create(&workers[i], NULL, add_to_global, (void *)&inputs[i]);
            }
            else
                while (--i > -1)
                    pthread_join(workers[i], NULL);
        }
        while (--i > -1)
            pthread_join(workers[i], NULL);
        printf("%s\n", pixels[y]); // if this is removed, this program will never finish
    }
}

int         main()
{
    thread_runner(0, WIDTH, 0, HEIGHT);
    return (0);
}

所以最初在尝试制作分形可视化器时遇到了这个问题,我可以使用这种一次运行十个线程的方法,前提是我在执行此操作时在屏幕上绘制图像。如果我让程序自行运行,它会变得完全没有响应,并且我的 mac 开始发出令人不安的噪音。我对线程还很陌生,所以这可能是我对它们的误解非常简单。

这个程序是我在不需要所有分形废话的情况下重现这种现象的尝试。这是一个完全没有意义的程序,它只是告诉你一个点的 x 坐标是否可以被 2 或 3 整除,并相应地将某些字符打印到终端。

如果您在处理完该行上的每个点 x 之后删除打印每一行 y 的 printf,程序将变得无响应。但是,使用这种定期打印运行程序可以使其正常工作。这似乎是一些编程恶魔在跟我开玩笑,让问题无法调试。

为什么这个问题被看似毫无意义的 printf 解决了?为什么我的 mac 在没有这个 printf 的情况下运行它时会发出令人不安的声音?

【问题讨论】:

  • 如果编程恶魔似乎在和你开玩笑,那么很有可能你正在执行未定义的行为,例如超出数组的边界或在结束后尝试使用对象它的生命周期。

标签: c pthreads pthread-join


【解决方案1】:

你只在你的if 上做++x,但不是else。因此,xwhile 循环可能永远不会完成。

我确实没有看到允许printf 改变线程的时间和交互的竞争条件,所以我有点困惑为什么你认为它有助于完成。当我最初运行您的代码时(使用 printf),我仍然遇到了一个无限循环。


编辑: 仔细查看后,我想出的修复并不能正确代表您的循环。它阻止了无限循环,但 没有 覆盖了所需的 x 范围。下面有一个更好的修复方法。

pthread_join 循环有通用代码。通过组合它并添加一个break,我[至少] 完成了。

这可能不是您的全部/最终解决方案,但它可能会有所帮助:

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

#define WIDTH   1000
#define HEIGHT  1000

typedef struct s_pair {
    int x;
    int y;
} t_pair;

char pixels[HEIGHT][WIDTH];

void *
add_to_global(void *param)
{
    t_pair *input = param;

    if (! (input->x % 2))
        pixels[input->y][input->x] = '.';
    else if (! (input->x % 3))
        pixels[input->y][input->x] = '#';
    else
        pixels[input->y][input->x] = ' ';

    return (NULL);
}

void
thread_runner(int x_start, int x_end, int y_start, int y_end)
{
    pthread_t workers[10];
    t_pair inputs[10];
    int i, x, y;

    y = y_start - 1;

    while (++y < y_end) {
        x = x_start - 1;
        i = -1;

        while (x < x_end) {
            if (++i >= 10)
                break;

            inputs[i] = (t_pair) { ++x, y };
            pthread_create(&workers[i], NULL, add_to_global, &inputs[i]);
        }

        while (--i > -1)
            pthread_join(workers[i], NULL);

        // if this is removed, this program will never finish
        printf("%s\n", pixels[y]);
    }
}

int
main(void)
{
    thread_runner(0, WIDTH, 0, HEIGHT);
    return (0);
}

更新:

这是一个更好的解决方法。

-1(例如y = y_start - 1)开始的循环有点不常见,它们会使代码复杂化,并且可能会因一个错误而中断。

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

#define WIDTH   1000
#define HEIGHT  1000

typedef struct s_pair {
    int x;
    int y;
} t_pair;

char pixels[HEIGHT][WIDTH];

void *
add_to_global(void *param)
{
    t_pair *input = param;

    if (! (input->x % 2))
        pixels[input->y][input->x] = '.';
    else if (! (input->x % 3))
        pixels[input->y][input->x] = '#';
    else
        pixels[input->y][input->x] = ' ';

    return (NULL);
}

void
thread_join(int i,pthread_t *workers)
{

    while (--i > -1)
        pthread_join(workers[i], NULL);
}

void
thread_runner(int x_start, int x_end, int y_start, int y_end)
{
    pthread_t workers[10];
    t_pair inputs[10];
    int i, x, y;

    for (y = y_start;  y < y_end;  ++y) {
        x = x_start;

        while (x < x_end) {
            for (i = 0;  (i < 10) && (x < x_end);  ++i, ++x) {
                inputs[i] = (t_pair) { x, y };
                pthread_create(&workers[i], NULL, add_to_global, &inputs[i]);
            }
            thread_join(i,workers);
            i = 0;
        }

        // NOTE: not really needed -- just to be safe
        thread_join(i,workers);

        // if this is removed, this program will never finish
#if 0
        printf("%s\n", pixels[y]);
#else
        printf("y=%d x=%d x_end=%d\n",y,x,x_end);
#endif
    }
}

int
main(void)
{
    thread_runner(0, WIDTH, 0, HEIGHT);
    return (0);
}

【讨论】:

    【解决方案2】:

    您正在读/写超出数组的末尾。

        x = x_start - 1;
        i = -1;
        while (x < x_end)
        {
            if (++i < 10)
            {
                inputs[i] = (t_pair){++x, y};
                pthread_create(&workers[i], NULL, add_to_global, (void *)&inputs[i]);
            }
            else
                while (--i > -1)
                    pthread_join(workers[i], NULL);
        }
    

    在此块中,外部循环的最后一次迭代具有 x 等于 x_end-1。然后,当您创建输入时,x 会递增,因此 inputs[i].xx_end。然后使用该值写入数组维度之一的末尾。

    这会触发 undefined behavior,您将其视为对 printf 的调用,从而导致/阻止无限循环。

    不要以 -1 开始循环并在循环条件或主体中间递增,而是从 0 开始并在结束时递增。

    void thread_runner(int x_start, int x_end, int y_start, int y_end)
    {
        pthread_t   workers[10];
        t_pair      inputs[10];
        int         i, x, y;
    
        y = 0;
        while (y < y_end)
        {
            i = 0;
            x = 0;
            while (x < x_end)
            {
                if (i < 10)
                {
                    inputs[i] = (t_pair){x, y};
                    pthread_create(&workers[i], NULL, add_to_global, &inputs[i]);
                    i++;
                }
                else {
                    while (--i > -1)
                        pthread_join(workers[i], NULL);
                    i=0;
                }
                x++;
            }
            while (--i > -1)
                pthread_join(workers[i], NULL);
            y++;
        }
    }
    

    我喜欢用来查找此类问题的一个技巧(我在本例中使用)是将所有固定数组更改为动态分配的内存。这有两件事:如果您做错了什么,它可能导致崩溃(更容易找到它们),它更好地允许像 valgrind 这样的工具准确检测正在发生的事情。

    【讨论】:

      猜你喜欢
      • 2023-03-22
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多