如何避免在更新 GTK UI 的 GTask 结束时出现竞争条件?
How do I avoid a race condition at the end of a GTask that updates a GTK UI?
我正在构建一个 gtk3 应用程序,并试图找出在更新 UI.
的 GTask
完成时避免竞争条件的方法
由于 GTask
调用的函数 (data_acq()
) 是 long-运行ning,我有一个更新的进度条(通过 gdk_threads_add_timeout(progressBar_timeout_cb)
),但是在 data_acq()
结束时,我使用 gdk_threads_add_idle(progressBar_complete, params)
将进度条设置为 100%。问题出现在 GTask
的最后一步是自动调用 free_data_acq_data(params)
释放指向进度条的指针,当 progressBar_complete(params)
触发时导致段错误,如果它发生 在 free_data_acq_data(params)
发生后。
这个问题可能完全是由于我滥用指针(或其他一些菜鸟错误)造成的,但我承认我没有看到如何以保证 1) 的方式传递数据在使用后被释放,并且 2) 它不会被释放得太早。请注意,任务有可能被取消,所以我不想释放 progressBar_complete()
.
中的内存
所以我的问题是:在这种情况下是否有更好的方法来避免竞争条件(或者是否有另一种方法来构建代码以避免此问题)?或者是否有一种机制可以检查 gdk_threads_add_idle()
函数是否已完成,以便我可以告诉内存释放函数等待 idle
完成?
我尝试使用全局来跟踪 progressBar_complete
是否有 运行,但我无法让它与 GTask
很好地配合,因为这意味着 free_data_acq_data
从未完成(因为线程从未放弃 progressBar_complete
以更新全局)。
为了具体起见,这里有一些示例代码:
#include <gtk/gtk.h>
struct dataAcqParams {
GtkWidget *progressBar;
int timeoutID;
GCancellable *cancellable;
};
void update_progressBar(GtkWidget *progressBar,
double fraction)
{
gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(progressBar), fraction);
}
int complete_progressBar(gpointer data)
{
struct dataAcqParams *params = data;
GtkWidget *progressBar;
progressBar = params->progressBar;
g_source_remove(params->timeoutID); // This turns off automatic progress bar updates
update_progressBar(progressBar, 1.0);
return G_SOURCE_REMOVE;
}
int progressBar_timeout_cb(gpointer data)
{
GtkWidget *progressBar;
progressBar = params->progressBar;
double fraction = 0.50; // For brevity
update_progressBar(progressBar, fraction);
return G_SOURCE_CONTINUE; // We keep calling this until we cancel it...
}
void free_data_acq_data(void *data)
{
struct dataAcqParams *params = data;
g_free(params);
}
int data_acq(struct dataAcqParams *data)
{
struct dataAcqParams *params = data;
g_print("Performing a measurement...\n");
g_usleep(10e5); // Some long computation, can be cancelled
gdk_threads_add_idle(complete_progressBar, params); // Race condition starts here
g_usleep(500000); // Delay to prevent race condition, surely there is a better way?
return 0;
}
static void data_acq_cb(GTask *task,
gpointer source_object,
gpointer task_data,
GCancellable *cancellable)
{
struct dataAcqParams *params = task_data;
int retval;
// Handle Cancellation:
if(g_task_return_error_if_cancelled(task))
{
return;
}
retval = data_acq(params);
g_task_return_int(task, retval);
}
void start_data_acq_async(gpointer data,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
GTask *task = NULL;
struct dataAcqParams *params;
params = (struct dataAcqParams *) data;
// Error if this is badly formatted:
g_return_if_fail(cancellable == NULL | G_IS_CANCELLABLE(cancellable));
task = g_task_new(NULL, cancellable, callback, user_data);
g_task_set_source_tag(task, start_data_acq_async);
g_task_set_return_on_cancel(task, FALSE);
g_task_set_task_data(task, params, free_data_acq_data);
// Run the acquisition in a worker thread:
g_task_run_in_thread(task, data_acq_cb);
g_object_unref(task);
}
int main(int argc, char **argv)
{
gtk_init(&argc, &argv);
// ... build/show ui ...
GtkWidget *progressBar;
GtkBuilder *builder;
// ...
progressBar = GTK_WIDGET(gtk_builder_get_object(builder, "progress_bar"));
GCancellable *cancellable;
struct dataAcqParams *params = g_malloc(sizeof(*params));
params->progressBar = progressBar;
params->cancellable = cancellable;
params->timeoutID = gdk_threads_add_timeout(100, progressBar_timeout_cb,
params);
start_data_acq_async(params, cancellable, NULL, NULL);
gtk_main();
return 0;
}
如果您需要从UI线程访问进度条指针,那么我建议不要在工作线程中释放它。也许当任务完成或取消时,您可以使用 gdk_threads_add_idle()
排队另一个函数,该函数将像 dataAcqParams
一样释放 UI 端使用的资源,而不是在 worker 中释放它线程?
我正在构建一个 gtk3 应用程序,并试图找出在更新 UI.
的GTask
完成时避免竞争条件的方法
由于 GTask
调用的函数 (data_acq()
) 是 long-运行ning,我有一个更新的进度条(通过 gdk_threads_add_timeout(progressBar_timeout_cb)
),但是在 data_acq()
结束时,我使用 gdk_threads_add_idle(progressBar_complete, params)
将进度条设置为 100%。问题出现在 GTask
的最后一步是自动调用 free_data_acq_data(params)
释放指向进度条的指针,当 progressBar_complete(params)
触发时导致段错误,如果它发生 在 free_data_acq_data(params)
发生后。
这个问题可能完全是由于我滥用指针(或其他一些菜鸟错误)造成的,但我承认我没有看到如何以保证 1) 的方式传递数据在使用后被释放,并且 2) 它不会被释放得太早。请注意,任务有可能被取消,所以我不想释放 progressBar_complete()
.
所以我的问题是:在这种情况下是否有更好的方法来避免竞争条件(或者是否有另一种方法来构建代码以避免此问题)?或者是否有一种机制可以检查 gdk_threads_add_idle()
函数是否已完成,以便我可以告诉内存释放函数等待 idle
完成?
我尝试使用全局来跟踪 progressBar_complete
是否有 运行,但我无法让它与 GTask
很好地配合,因为这意味着 free_data_acq_data
从未完成(因为线程从未放弃 progressBar_complete
以更新全局)。
为了具体起见,这里有一些示例代码:
#include <gtk/gtk.h>
struct dataAcqParams {
GtkWidget *progressBar;
int timeoutID;
GCancellable *cancellable;
};
void update_progressBar(GtkWidget *progressBar,
double fraction)
{
gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(progressBar), fraction);
}
int complete_progressBar(gpointer data)
{
struct dataAcqParams *params = data;
GtkWidget *progressBar;
progressBar = params->progressBar;
g_source_remove(params->timeoutID); // This turns off automatic progress bar updates
update_progressBar(progressBar, 1.0);
return G_SOURCE_REMOVE;
}
int progressBar_timeout_cb(gpointer data)
{
GtkWidget *progressBar;
progressBar = params->progressBar;
double fraction = 0.50; // For brevity
update_progressBar(progressBar, fraction);
return G_SOURCE_CONTINUE; // We keep calling this until we cancel it...
}
void free_data_acq_data(void *data)
{
struct dataAcqParams *params = data;
g_free(params);
}
int data_acq(struct dataAcqParams *data)
{
struct dataAcqParams *params = data;
g_print("Performing a measurement...\n");
g_usleep(10e5); // Some long computation, can be cancelled
gdk_threads_add_idle(complete_progressBar, params); // Race condition starts here
g_usleep(500000); // Delay to prevent race condition, surely there is a better way?
return 0;
}
static void data_acq_cb(GTask *task,
gpointer source_object,
gpointer task_data,
GCancellable *cancellable)
{
struct dataAcqParams *params = task_data;
int retval;
// Handle Cancellation:
if(g_task_return_error_if_cancelled(task))
{
return;
}
retval = data_acq(params);
g_task_return_int(task, retval);
}
void start_data_acq_async(gpointer data,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
GTask *task = NULL;
struct dataAcqParams *params;
params = (struct dataAcqParams *) data;
// Error if this is badly formatted:
g_return_if_fail(cancellable == NULL | G_IS_CANCELLABLE(cancellable));
task = g_task_new(NULL, cancellable, callback, user_data);
g_task_set_source_tag(task, start_data_acq_async);
g_task_set_return_on_cancel(task, FALSE);
g_task_set_task_data(task, params, free_data_acq_data);
// Run the acquisition in a worker thread:
g_task_run_in_thread(task, data_acq_cb);
g_object_unref(task);
}
int main(int argc, char **argv)
{
gtk_init(&argc, &argv);
// ... build/show ui ...
GtkWidget *progressBar;
GtkBuilder *builder;
// ...
progressBar = GTK_WIDGET(gtk_builder_get_object(builder, "progress_bar"));
GCancellable *cancellable;
struct dataAcqParams *params = g_malloc(sizeof(*params));
params->progressBar = progressBar;
params->cancellable = cancellable;
params->timeoutID = gdk_threads_add_timeout(100, progressBar_timeout_cb,
params);
start_data_acq_async(params, cancellable, NULL, NULL);
gtk_main();
return 0;
}
如果您需要从UI线程访问进度条指针,那么我建议不要在工作线程中释放它。也许当任务完成或取消时,您可以使用 gdk_threads_add_idle()
排队另一个函数,该函数将像 dataAcqParams
一样释放 UI 端使用的资源,而不是在 worker 中释放它线程?