使用 GTK 和 C,如何使用按钮开始/停止长计算(在单独的线程中)?我有可以做到这一点的工作代码,但我不太相信这是一种合理的方法(即“正确”)。
我有一个按钮,其标签从“开始”切换到“停止”。我还有一个全局 pthread_t 变量来存储线程。我的方法是通过按钮的单击信号处理程序启动或取消线程,具体取决于全局布尔型“空闲”标志的值,该标志指示线程当前是否正在运行。
我想要一个设计良好的最小测试用例,以便我可以轻松理解代码以适应更大的程序。这个问题与Python&PyGTK: Stop while on button click非常相似,但这个问题在我不知道的 python 中。
我的代码 --- 发布在下面 --- 似乎可以工作,但我对它没有信心,因为我可以通过快速连续几次单击开始/停止按钮轻松地使系统瘫痪。
我很想知道其他人将如何(独立地)解决这个问题,他们的方法与我的方法相比如何,如果它实际上是一种体面的方法,我也会对我自己的方法进行代码审查。
#include <gtk/gtk.h>
#include <pthread.h>
/* suppress unused variable warnings */
#define UNUSED(x) (void)(x)
typedef struct _Data {
GtkWidget *window1,
*button1;
gint idle;
pthread_t calcthread;
} Data;
static Data *data;
void *calcfunc(void *arg) {
int i;
UNUSED(arg);
data->idle=FALSE;
gtk_button_set_label(GTK_BUTTON(data->button1),"Stop");
/* This is intended to simulated a long calculation that may finish.
Adjust the limit as needed */
for(i=1;i<2e9;++i) {
}
data->idle=TRUE;
pthread_exit(NULL);
}
/* this is our click event handler.... it suppose to start or stop
the "calcthread" depending on the value of the "idle" flag */
void on_button1_clicked(GtkWidget *widget, Data *ldata) {
int ret;
UNUSED(widget);
UNUSED(ldata);
if ( data->idle==TRUE ) {
printf("idle.. starting thread\n");
ret=pthread_create( &data->calcthread, NULL, calcfunc, NULL);
if ( ret !=0 ) {
g_error("ERROR: could not create thread\n");
}
} else {
printf("not idle... canceling thread...");
ret= pthread_cancel( data->calcthread );
if ( ret != 0 ) {
g_error("ERROR: could not cancel thread\n");
} else {
printf("canceled\n");
}
data->idle=TRUE;
gtk_button_set_label(GTK_BUTTON(data->button1),"start");
}
}
/* just defines our setup */
int main (int argc, char *argv[]) {
g_thread_init(NULL);
gdk_threads_init();
gdk_threads_enter();
gtk_init(&argc, &argv);
data=g_slice_new0(Data);
data->idle=TRUE; /* initial state */
printf("idle is %d\n",data->idle);
/* add widgets and objects to our structure */
data->window1=gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_window_set_default_size(GTK_WINDOW(data->window1),250,250);
data->button1=gtk_button_new_with_label("Start");
gtk_container_add(GTK_CONTAINER(data->window1),GTK_WIDGET(data->button1));
gtk_signal_connect(GTK_OBJECT(data->window1), "delete-event",
gtk_main_quit, NULL);
gtk_signal_connect(GTK_OBJECT(data->button1), "clicked",
G_CALLBACK(on_button1_clicked), NULL);
gtk_widget_show_all(GTK_WIDGET(data->window1));
gtk_main();
/* Don't forget to free the memory! */
g_slice_free(Data, data);
gdk_threads_leave();
return 0;
}