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;
}