[英]How do I avoid a race condition at the end of a GTask that updates a GTK UI?
我正在構建一個 gtk3 應用程序,並試圖避免在完成更新 UI 的GTask
時出現競爭條件。
由於 GTask 調用的GTask
( data_acq data_acq()
)長時間運行,我有一個更新的進度條(通過gdk_threads_add_timeout(progressBar_timeout_cb)
),但在 data_acq( data_acq()
結束時,我使用gdk_threads_add_idle(progressBar_complete, params)
來設置進度條到 100%。 問題出現在GTask
的最后一步是自動調用free_data_acq_data(params)
釋放指向進度條的指針,如果在free_data_acq_data(params)
發生之后發生,則在progressBar_complete(params)
觸發時會導致段錯誤。
這個問題可能完全是由於我濫用指針(或其他一些新手錯誤),但我承認我沒有看到如何以保證 1)它會在使用后被釋放的方式傳遞數據並且2)它不會被過早釋放。 請注意,任務可能被取消,因此我不想在progressBar_complete()
中釋放 memory 。
所以我的問題是:在這種情況下是否有更好的方法來避免競爭條件(或者是否有另一種方法來構造代碼以避免這個問題)? 或者是否有檢查gdk_threads_add_idle()
function 是否已完成的機制,所以我可以告訴釋放內存的 function 等到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()
排隊另一個 function ,這將釋放 UI 端使用的資源,如dataAcqParams
,而不是在工作線程中釋放它?
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.