gpt4 book ai didi

c - 我的线程图像生成应用程序如何将其数据获取到 gui?

转载 作者:塔克拉玛干 更新时间:2023-11-02 23:12:28 27 4
gpt4 key购买 nike

Mandelbrot 生成器的慢速多精度实现。线程化,使用 POSIX 线程。 Gtk 图形用户界面。

我有点迷路了。这是我第一次尝试编写线程程序。我实际上还没有尝试转换它的单线程版本,只是尝试实现基本框架。

到目前为止它是如何工作的简要说明:

Main 创建 watch_render_start 线程,它等待 pthread_cond_signal,当“渲染”按钮被点击时,GUI 回调会发出信号。

watch_render_start 检查图像是否已经在渲染,检查退出等,但如果一切顺利,它会创建 render_create_threads 线程。

render_create_threads 线程然后创建渲染线程,然后使用 pthread_join 等待它们完成(并使用 get_time_of_day 做一些计时工作——这在线程中很糟糕吗?)。

渲染线程的入口点(想象中)称为 render,循环,而 next_line 计算函数返回 TRUE 以处理更多行。在这个 while 循环中,检查停止或退出。

next_line 函数获取要计算的行,然后递增变量以指示下一个线程要计算的下一行。如果要处理的行超出图像高度,它会返回。如果不是,则它计算该行的内容。然后递增 lines_done 并根据图像的高度检查它,如果 >= 则返回 0,如果 <.

则返回 1

这里是全部 470 多行代码,我相信您会从中获得乐趣。

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <mpfr.h>
#include <string.h>
#include <gtk/gtk.h>
#include <sys/time.h>

/* build with:

gcc threaded_app.c -o threaded_app -Wall -pedantic -std=gnu99 -lgmp -lmpfr -pthread -D_REENTRANT -ggdb `pkg-config --cflags gtk+-2.0` `pkg-config --libs gtk+-2.0`

*/

typedef struct
{
struct timeval tv_start;
struct timeval tv_end;
} Timer;

void timer_start(Timer* t)
{
gettimeofday(&t->tv_start, 0);
}

void timer_stop(Timer* t)
{
gettimeofday(&t->tv_end, 0);
}

long timer_get_elapsed(Timer* t)
{
if (t->tv_start.tv_sec == t->tv_end.tv_sec)
return t->tv_end.tv_usec - t->tv_start.tv_usec;
else
return (t->tv_end.tv_sec - t->tv_start.tv_sec) *
1e6 + (t->tv_end.tv_usec - t->tv_start.tv_usec);
}

#define NTHREADS 8

#define IMG_WIDTH 480
#define IMG_HEIGHT 360

typedef struct
{
int rc;
pthread_t thread;
} rthrds;

typedef struct
{
int* arr;
int next_line;
int lines_done;
int rendering;
int start;
int stop;
pthread_t rend[NTHREADS];

int all_quit;

int width;
int height;

double xmin, xmax, ymax;
int depth;

} image_info;


static gboolean delete_event(GtkWidget *widget,
GdkEvent *event,
gpointer data);
static void destroy(GtkWidget *widget, gpointer data);

void gui_start_render(GtkWidget* widget, gpointer data);
void gui_stop_render(GtkWidget* widget, gpointer data);

static GtkWidget* gui_pbar = NULL;

void *render(void* ptr);
int next_line(image_info* img);

void* watch_render_start(void* ptr);
void* watch_render_stop(void* ptr);
void* watch_render_done(void* ptr);

void* threads_render_create(void* ptr);

pthread_mutex_t next_line_mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_t lines_done_mutex = PTHREAD_MUTEX_INITIALIZER;

pthread_mutex_t img_start_mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_t img_stop_mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_t img_rendering_mutex = PTHREAD_MUTEX_INITIALIZER;

pthread_cond_t img_start_cond = PTHREAD_COND_INITIALIZER;
pthread_cond_t img_stop_cond = PTHREAD_COND_INITIALIZER;
pthread_cond_t img_done_cond = PTHREAD_COND_INITIALIZER;

pthread_mutex_t all_quit_mutex = PTHREAD_MUTEX_INITIALIZER;

int main(int argc, char **argv)
{
printf("initializing...\n");
image_info* img = malloc(sizeof(image_info));
memset(img, 0, sizeof(image_info));

img->start = 0;

img->width = IMG_WIDTH;
img->height = IMG_HEIGHT;

img->xmin = -0.75509089265046296296296259;
img->xmax = -0.75506025752314814814814765;
img->ymax = 0.050215494791666666666666005;
img->depth = 30000;

size_t arr_size = img->width * img->height * sizeof(int);

printf("creating array size: %ld bytes\n", arr_size);
img->arr = malloc(arr_size);
if (!img->arr)
{
fprintf(stderr, "image dimension too large!\n");
free(img);
exit(-1);
}
memset(img->arr, 0, arr_size);

int rc_err;
pthread_t thread_start;
pthread_attr_t attr;
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);

printf("creating watch render start thread...\n");

rc_err = pthread_create(&thread_start, &attr,
&watch_render_start, (void*)img);
if (rc_err)
{
fprintf(stderr, "Thread start creation failed: %d\n",
rc_err);
free(img->arr);
free(img);
exit(-1);
}

printf("creating GUI...\n");

GtkWidget *window;
GtkWidget *startbutton;
GtkWidget *stopbutton;
GtkWidget *box1;
gtk_init (&argc, &argv);
window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
g_signal_connect (G_OBJECT (window), "delete_event",
G_CALLBACK (delete_event), NULL);
g_signal_connect (G_OBJECT (window), "destroy",
G_CALLBACK (destroy), NULL);
gtk_container_set_border_width (GTK_CONTAINER (window), 10);

box1 = gtk_hbox_new(FALSE, 0);
gtk_container_add(GTK_CONTAINER(window), box1);

startbutton = gtk_button_new_with_label ("Start render");
g_signal_connect (G_OBJECT (startbutton), "clicked",
G_CALLBACK (gui_start_render), img);
gtk_box_pack_start(GTK_BOX(box1), startbutton, TRUE, TRUE, 0);
stopbutton = gtk_button_new_with_label ("Stop render");
g_signal_connect (G_OBJECT (stopbutton), "clicked",
G_CALLBACK (gui_stop_render), img);
gtk_box_pack_start(GTK_BOX(box1), stopbutton, TRUE, TRUE, 0);

gui_pbar = gtk_progress_bar_new();
gtk_progress_bar_set_orientation(GTK_PROGRESS_BAR(gui_pbar),
GTK_PROGRESS_LEFT_TO_RIGHT);
gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR(gui_pbar),
(gfloat)1.0 ); /* img->real_height); */
gtk_widget_set_size_request(gui_pbar, 75, 0);
gtk_box_pack_end(GTK_BOX(box1), gui_pbar, FALSE, FALSE, 0);

gtk_widget_show(startbutton);
gtk_widget_show(stopbutton);
gtk_widget_show(box1);
gtk_widget_show(window);

printf("starting GUI\n");

gtk_main ();

printf("************************\n"
"GUI shutdown\n"
"************************\n");

printf("setting all_quit\n");

pthread_mutex_lock(&all_quit_mutex);
img->all_quit = 1;
pthread_mutex_unlock(&all_quit_mutex);

printf("signalling watch render start thread to wakeup...\n");

pthread_mutex_lock(&img_start_mutex);
pthread_cond_signal(&img_start_cond);
pthread_mutex_unlock(&img_start_mutex);

printf("waiting for watch render start thread to quit...\n");

pthread_join(thread_start, NULL);

printf("done\n");

printf("freeing memory\n");

free(img->arr);
free(img);

printf("goodbye!\n");

exit(0);
}

void gui_start_render(GtkWidget* widget, gpointer ptr)
{
image_info* img = (image_info*)ptr;

printf("************\n"
"GUI signalling to start render...\n"
"************\n");

pthread_mutex_lock(&img_start_mutex);
img->start = 1;
pthread_cond_signal(&img_start_cond);
pthread_mutex_unlock(&img_start_mutex);
}

void gui_stop_render(GtkWidget* widget, gpointer ptr)
{
image_info* img = (image_info*)ptr;

printf("************\n"
"GUI signalling to stop render...\n"
"************\n");

pthread_mutex_lock(&img_stop_mutex);
img->stop = 1;
pthread_mutex_unlock(&img_stop_mutex);
}

void* watch_render_start(void* ptr)
{
image_info* img = (image_info*)ptr;

int rc_err;
pthread_t render_thread;
pthread_attr_t attr;
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);

int r;

int quit = 0;

for(;;)
{
printf("watch_render_start: waiting for img_start_cond\n");
pthread_mutex_lock(&img_start_mutex);
if (!img->start)
pthread_cond_wait(&img_start_cond, &img_start_mutex);
img->start = 0;
pthread_mutex_unlock(&img_start_mutex);
printf("watch_render_start: recieved img_start_cond\n");

pthread_mutex_lock(&img_rendering_mutex);
r = img->rendering;
pthread_mutex_unlock(&img_rendering_mutex);

printf("checking if we are rendering... ");

if (r)
{
printf("yes\nStopping render...\n");
pthread_mutex_lock(&img_stop_mutex);
img->stop = 1;
pthread_cond_signal(&img_stop_cond);
pthread_mutex_unlock(&img_stop_mutex);
pthread_join(render_thread, NULL);
printf("render stopped\n");
}
else
printf("no\n");

pthread_mutex_lock(&all_quit_mutex);
quit = img->all_quit;
pthread_mutex_unlock(&all_quit_mutex);

if (quit)
{
printf("exiting watch render start thread\n");
pthread_exit(0);
}

printf("creating render thread...\n");
rc_err = pthread_create(&render_thread, &attr,
&threads_render_create, (void*)img);
if (rc_err)
pthread_exit(0);
}
}

void* threads_render_create(void* ptr)
{
Timer timing_info;

printf("initializing render thread\n");

image_info* img = (image_info*)ptr;

pthread_mutex_lock(&img_rendering_mutex);

img->rendering = 1;
pthread_mutex_unlock(&img_rendering_mutex);

pthread_mutex_lock(&lines_done_mutex);
img->lines_done = 0;
pthread_mutex_unlock(&lines_done_mutex);

pthread_mutex_lock(&img_stop_mutex);
img->stop = 0;
pthread_mutex_unlock(&img_stop_mutex);

pthread_mutex_lock(&next_line_mutex);
img->next_line = 0;
pthread_mutex_unlock(&next_line_mutex);

int rc_err, i;
pthread_attr_t attr;
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);

timer_start(&timing_info);

for (i = 0; i < NTHREADS; ++i)
{
printf("creating renderer thread #%d...\n", i);
rc_err = pthread_create(&img->rend[i], &attr,
&render, (void*)img);
if (rc_err)
{
fprintf(stderr, "\nrender thread #%d creation failed: %d\n",
i, rc_err);
return 0;
}
}

for (i = 0; i < NTHREADS; ++i)
{
printf("joining renderer thread #%d...\n", i);
pthread_join(img->rend[i], NULL);
}

timer_stop(&timing_info);
printf("render-time %.3fs\n\n",
timer_get_elapsed(&timing_info) / (double)1e6);

printf("all renderer threads finished\n");

pthread_mutex_lock(&img_stop_mutex);
img->stop = 0;
pthread_mutex_unlock(&img_stop_mutex);

pthread_mutex_lock(&img_rendering_mutex);
img->rendering = 0;
pthread_mutex_unlock(&img_rendering_mutex);

printf("at end of threads_render_create\n");
pthread_mutex_lock(&lines_done_mutex);
if (img->lines_done >= img->height)
printf("image complete\n");
else
printf("image interuppted\n");
pthread_mutex_unlock(&lines_done_mutex);

pthread_mutex_lock(&img_start_mutex);
img->start = 0;
pthread_mutex_unlock(&img_start_mutex);
printf("exiting render thread\n");
pthread_exit(NULL);
}

void* render(void* ptr)
{
image_info* img = (image_info*)ptr;
int quit = 0;
printf("starting render..\n");
while(next_line(img) && !quit)
{
pthread_mutex_lock(&img_stop_mutex);
quit = img->stop;
pthread_mutex_unlock(&img_stop_mutex);
pthread_mutex_lock(&all_quit_mutex);
quit |= img->all_quit;
pthread_mutex_unlock(&all_quit_mutex);
}
printf("exiting render thread\n");
pthread_exit(0);
}

int next_line(image_info* img)
{
int line;

pthread_mutex_lock(&next_line_mutex);
line = img->next_line++;
pthread_mutex_unlock(&next_line_mutex);

if (line >= img->height)
return 0;

int ix,wz;
int img_width = img->width;
long double x,y,x2,y2,wre=0,wim=0,wre2=0,wim2=0;
long double xmin = img->xmin, xmax = img->xmax, ymax = img->ymax;
long double xdiff = xmax - xmin;
int depth = img->depth;
long double c_im = 0, c_re = 0;

y = ymax - (xdiff / (long double)img_width)
* (long double)line;
y2 = y * y;

for (ix = 0; ix < img_width; ++ix)
{
x = ((long double)ix / (long double)img_width) * xdiff + xmin;
x2 = x * x;
wre = x;
wim = y;
wre2 = x2;
wim2 = y2;
for (wz = 0; wz < depth; ++wz)
{
wim = 2.0 * wre * wim + c_im;
wre = wre2 - wim2 + c_re;
wim2 = wim * wim;
wre2 = wre * wre;
if (wim2 + wre2 > 4.0F)
break;
}
if (wz == depth + 1)
wz = 0;
img->arr[line * img_width + ix] = wz;
}

printf("line %d complete\n", line);

pthread_mutex_lock(&lines_done_mutex);
img->lines_done++;
if (img->lines_done == img->height)
{
pthread_mutex_unlock(&lines_done_mutex);
return 0;
}
pthread_mutex_unlock(&lines_done_mutex);

return 1;
}

static gboolean delete_event(GtkWidget *widget,
GdkEvent *event,
gpointer data)
{
return FALSE;
}

static void destroy(GtkWidget *widget, gpointer data)
{
gtk_main_quit ();
}

我已经走到这一步了,需要一些关于如何继续的指导。对于我面临的每个问题,我只是看到一个令人困惑的解决方案迷宫,通向死胡同!

我想先解决进度条问题。 gui 需要锁定 lines_done。但是怎么知道什么时候该做呢?它多久查看一次 lines_done?我想我可以为此使用 g_idle_add。

然后是实际呈现所有这些快乐线程正在生成的数据的真正肉质问题。正如在另一个问题上所讨论的,我将有一组标志来指示实际呈现的行(因为由于线程和操作系统调度程序的性质,它们将以任意顺序呈现)。但是 GUI 将如何检查这些呢?在与进度条相同的空闲回调中?假设正在生成一个 8000 像素高的大图像,那就是每隔这么多毫秒就有 8000 个互斥锁和解锁 - 这一定是有成本的吧?

那么我该如何进行呢?我正在使用的这个模型,无论它是什么,是否能够做我想做的事?

最佳答案

如果您可以在您的平台上访问原子读取和原子写入,那么创建一个工作分配表(阅读您平台的体系结构说明 - 普通读取和写入可能足够好,也可能不够好,你可能需要也可能不需要添加内存屏障):

每行一个字节,初始为零,非零表示该行分配给的线程

...并为每个工作线程创建一个自动更新的完成行数 int 字段。应使用原子读/写指令更新和读取该表(根据平台上的可用指令,以 8、16、32 或 64 位的 block 形式)。

顶层逻辑必须确定是直接在主线程上完成所有工作(如果图像真的很小),还是启动一个工作线程,或者启动 N 个工作线程。

协调线程(或者如果是我,我可能会放弃协调线程并在主线程上执行此操作)将循环法中的一半工作分配给线程(如果少于一定数量,则分配所有工作).如果它分配的工作量少于所有工作量,它就会监视其他线程并对平均线程和最佳线程的性能进行基准测试。它确保线程作为一个整体不会耗尽工作,但尽量不让线程无事可做。

前端为每个 worker 保留一个指针,指向 worker 在分配表中完成的位置,当 worker 增加其整数字段以表示它完成了多少行时,前端会向前搜索工作分配表查找分配给该 worker 的工作的行索引,现在已完成,并更新完成特定行的位缓冲区,并更新总完成字段。

--

这是将工作动态分配给线程的通用算法,正如另一位发帖人所建议的那样,您也可以通过使工作线程应处理的行号成为工作线程数和工作线程数的函数来静态分配工作线程数,然后通过原子字段将每个工作人员完成的行数传递给前端。

关于c - 我的线程图像生成应用程序如何将其数据获取到 gui?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/2035758/

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