问题描述
#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,HEIGHT);
return (0);
}
因此,最初在尝试制作分形可视化器时遇到了这个问题,只有当我在屏幕上绘制图像时,我才能使用这种一次运行 10 个线程的方法。如果我让程序自己运行,它会变得完全没有响应,我的 mac 开始发出令人不安的噪音。我对线程还是个新手,所以这可能是我对它们的误解很简单。
这个程序是我尝试在不需要所有分形废话的情况下重现这种现象。这是一个完全没有意义的程序,它只是告诉你一个点的 x 坐标是否可以被 2 或 3 整除,并相应地将某些字符打印到终端。
如果在处理完该行上的每个点 x 后删除打印每一行 y 的 printf,程序将变得无响应。但是,通过这种定期打印运行程序可以使其工作。这似乎是一些编程恶魔在和我玩诡计,使问题无法调试。
为什么这个问题被一个看似毫无意义的 printf 解决了?为什么我的 mac 在没有这个 printf 的情况下运行时会发出令人不安的声音?
解决方法
您正在读取/写入数组的末尾。
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].x
为 x_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],&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 这样的工具准确地检测正在发生的事情。
,您只在 ++x
上执行 if
,但不是 else
。因此,while
的 x
循环可能永远不会完成。
我没有看到允许 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 y_end)
{
pthread_t workers[10];
t_pair inputs[10];
int i,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],&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,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,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],&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_end);
#endif
}
}
int
main(void)
{
thread_runner(0,HEIGHT);
return (0);
}