问题描述
我正在制作一个光线追踪器,我正在尝试使用 pthread 来划分渲染。我注意到这对速度没有帮助,因为函数 pthread_join 很慢,如果我使用循环来使“await”更快并且几乎每次都能正常工作。但我不能使用它,因为渲染时间随场景而变化。 有没有办法以更有效的方式检查线程是否完成。这是代码。 `
int threats(t_file *c) //this function creates the threads
{
int i;
int err;
pthread_t th[THREADS];
i = 0;
printf("1\n");
c->thread = -1;
mlx_clear_window(c->mlx_ptr,c->win_ptr);
while (i < THREADS)
{
err = pthread_create(&th[i],(void *)paint_scene,(void *)c);
if (err)
return parse_error("Thread Error: CAN NOT CREATE THREAD");
i++;
}
// while (i-- >= 0)
// pthread_join(th[i],0);
//my await function xd
while (i < 200000000)
i++;
mlx_put_image_to_window(c->mlx_ptr,c->win_ptr,c->img.mlx_img,0);
c->thread = 0;
return 1;
}
void paint_scene(void *a)
{
int y;
int x;
t_ray ray;
int color;
t_file *c;
c = (t_file *)a;
color = 0;
c->thread++;
y = (c->thread * (c->win_heigth / THREADS));
printf("y:%d,hilo%d\n",y,c->thread);
while (y < (c->thread + 1) * (c->win_heigth / THREADS))
{
x = 0;
while (x < c->win_width)
{
ray = generate_ray(x,*c);
color = get_intersections(&ray,c);
if (c->ligth)
color = shading(&ray,color,c);
my_mlx_pixel_put(&c->img,x,color);
x++;
}
//ft_printf("\rLoading%d: %d%%",c->thread,y / (c->win_heigth / 100));
y++;
}
pthread_exit(0);
}
`
解决方法
您的线程函数中存在并发问题:
c->thread++;
y = (c->thread * (c->win_heigth / THREADS));
printf("y:%d,hilo%d\n",y,c->thread);
while (y < (c->thread + 1) * (c->win_heigth / THREADS))
{
....
}
c->thread
在所有线程之间共享,并且基于可能的线程计时和当前的月球表面,我可以做出有根据的猜测并说第一个线程正在计算整个图像。启动时,第一个线程可能会看到 c->thread == -1
,但稍后(如果线程启动比 while
循环快)其他线程增加该值,直到第一个线程看到 c->thread == THREADS-1
要解决这个问题,每次调用 create_thread
都必须传递一个指向唯一参数对象的指针,该对象包含该线程 ID。因此,从 thread
中删除 t_file
成员。它在那里可能没有任何意义。并创建一个 struct
类型来保存线程函数的参数:
struct thread_param
{
unsigned int thread;
file_t *c;
}
你在启动线程时像这样使用它:
struct thread_param params[THREADS];
while (i < THREADS)
{
params[i].thread = i;
params[i].c = c;
err = pthread_create(&th[i],(void *)paint_scene,(void *)&(params[i]));
if (err)
return parse_error("Thread Error: CAN NOT CREATE THREAD");
i++;
}
然后您访问线程函数中的数据:
void paint_scene(void *a)
{
struct thread_param *param = (struct thread_param *)a;
unsigned int thread = param->thread;
t_file *c = param->c;
/*
in the rest of the code you remove `c->thread++`
and replace `c->thread` with `thread`
*/
....
}
,
如果您有原子数据类型(C11,#ifndef __STDC_NO_ATOMICS__
),那么实现一个全局计数器并等待它达到零(如果减少)或线程数量(如果增加)。
例如
#include <stdatomic.h>
atomic_int num_jobs;
void* thread_func(void*)
{
//the work to do in the thread function
//before exit decrease counter
--num_jobs;
pthread_exit(0);
}
int main()
{
num_jobs = THREADS; // same as your number of threads
create_threads(THREADS); // number of threads = THREADS
while (num_jobs) { // loop while threads running
//sleep for a while
}
join_threads(); // join threads for cleanup
return 0;
}
其他经典的锁机制,
例如
#include <pthread.h>
pthread_spinlock_t lock;
int num_jobs;
// called by main
int numJobs()
{
pthread_spin_lock(&lock);
int res = num_jobs;
pthread_spin_unlock(&lock);
return res;
}
// called by thread_func
void decNumJobs()
{
pthread_spin_lock(&lock);
--num_jobs;
pthread_spin_unlock(&lock);
}
int main()
{
pthread_spin_init(&lock,PTHREAD_PROCESS_PRIVATE);
// the other stuff as before
pthread_spin_destroy(&lock);
return 0;
}
另一种选择是使用 pthread_cond_wait
和 pthread_cond_signal
(主要是为了避免 while 循环中的睡眠,在接收到信号后继续而不是基于固定的时间量)。
例如
#include <pthread.h>
int num_jobs;
pthread_cond_t cond;
pthread_mutex_t lock;
void decNumJobs()
{
pthread_mutex_lock(&lock);
if (--num_jobs == 0)
pthread_cond_signal(&cond);
pthread_mutex_unlock(&lock);
}
void* thread_func(void*)
{
//the work to do in the thread function
//before exit decrease counter
decNumJobs();
pthread_exit(0);
}
int main()
{
num_jobs = THREADS;
pthread_cond_init(&cond,NULL);
pthread_mutex_init(&lock,NULL);
pthread_mutex_lock(&lock);
create_threads(THREADS);
pthread_cond_wait(&cond,&lock);
pthread_mutex_unlock(&lock);
join_threads();
pthread_cond_destroy(&cond);
pthread_mutex_destroy(&lock);
return 0;
}
注意:为了简单起见,没有错误检查或处理。强烈建议阅读 pthread_*
函数(返回值、中断等待等)的文档。