add tk_is_ui_thread and tk_run_in_ui_thread

This commit is contained in:
lixianjing 2020-09-30 09:17:04 +08:00
parent 59d6b72f71
commit 1b20677c6e
6 changed files with 279 additions and 54 deletions

View File

@ -33,19 +33,7 @@
#include "base/image_manager.h"
#include "widgets/progress_bar.h"
static ret_t update_label(widget_t* label) {
char str[64];
static int times = 0;
tk_snprintf(str, sizeof(str), "times:%d", times++);
widget_set_text_utf8(label, str);
return RET_OK;
}
static ret_t on_timer(const timer_info_t* timer) {
return update_label(WIDGET(timer->ctx));
}
static bool_t s_app_quit = FALSE;
static ret_t update_progress_bar(widget_t* progress_bar) {
static bool_t inc = TRUE;
@ -66,13 +54,33 @@ static ret_t update_progress_bar(widget_t* progress_bar) {
return RET_OK;
}
void* test_thread1(void* args) {
int nr = 500000;
while ((nr-- > 0) && (!s_app_quit)) {
tk_run_in_ui_thread((tk_callback_t)update_progress_bar, args, TRUE);
sleep_ms(30);
}
return NULL;
}
void* test_thread2(void* args) {
int nr = 500000;
while ((nr-- > 0) && (!s_app_quit)) {
tk_run_in_ui_thread((tk_callback_t)update_progress_bar, args, FALSE);
sleep_ms(30);
}
return NULL;
}
static ret_t on_idle(const idle_info_t* idle) {
return update_progress_bar(WIDGET(idle->ctx));
}
void* test_idle_queue(void* args) {
void* test_thread3(void* args) {
int nr = 500000;
while (nr-- > 0) {
while ((nr-- > 0) && (!s_app_quit)) {
idle_queue(on_idle, args);
sleep_ms(30);
}
@ -80,9 +88,13 @@ void* test_idle_queue(void* args) {
return NULL;
}
void* test_timer_queue(void* args) {
static ret_t on_timer(const timer_info_t* timer) {
return update_progress_bar(WIDGET(timer->ctx));
}
void* test_thread4(void* args) {
int nr = 500000;
while (nr-- > 0) {
while ((nr-- > 0) && (!s_app_quit)) {
timer_queue(on_timer, args, 30);
sleep_ms(30);
}
@ -90,25 +102,61 @@ void* test_timer_queue(void* args) {
return NULL;
}
static tk_thread_t* thread1 = NULL;
static tk_thread_t* thread2 = NULL;
static tk_thread_t* thread3 = NULL;
static tk_thread_t* thread4 = NULL;
static ret_t on_close(void* ctx, event_t* e) {
s_app_quit = TRUE;
tk_quit();
return RET_OK;
}
ret_t application_init() {
tk_thread_t* thread = NULL;
widget_t* progress_bar = NULL;
widget_t* win = window_create(NULL, 0, 0, 0, 0);
widget_t* label = label_create(win, 10, 10, 300, 20);
widget_t* label = label_create(win, 0, 0, 0, 0);
widget_t* close = button_create(win, 0, 0, 0, 0);
widget_t* progress_bar1 = progress_bar_create(win, 0, 0, 0, 0);
widget_t* progress_bar2 = progress_bar_create(win, 0, 0, 0, 0);
widget_t* progress_bar3 = progress_bar_create(win, 0, 0, 0, 0);
widget_t* progress_bar4 = progress_bar_create(win, 0, 0, 0, 0);
widget_set_text(label, L"Update progressbar in non GUI thread");
progress_bar = progress_bar_create(win, 10, 80, 300, 20);
widget_set_self_layout_params(label, "center", "middle:-90", "80%", "30");
thread = tk_thread_create(test_idle_queue, progress_bar);
tk_thread_start(thread);
widget_set_self_layout_params(progress_bar1, "center", "middle:-60", "80%", "20");
widget_set_self_layout_params(progress_bar2, "center", "middle:-30", "80%", "20");
widget_set_self_layout_params(progress_bar3, "center", "middle:0", "80%", "20");
widget_set_self_layout_params(progress_bar4, "center", "middle:30", "80%", "20");
thread = tk_thread_create(test_timer_queue, label);
tk_thread_start(thread);
widget_set_text(close, L"close");
widget_set_self_layout_params(close, "center", "bottom:30", "50%", "30");
thread1 = tk_thread_create(test_thread1, progress_bar1);
tk_thread_start(thread1);
thread2 = tk_thread_create(test_thread2, progress_bar2);
tk_thread_start(thread2);
thread3 = tk_thread_create(test_thread3, progress_bar3);
tk_thread_start(thread3);
thread4 = tk_thread_create(test_thread4, progress_bar4);
tk_thread_start(thread4);
widget_on(close, EVT_CLICK, on_close, NULL);
widget_layout(win);
return RET_OK;
}
ret_t application_exit() {
tk_thread_destroy(thread1);
tk_thread_destroy(thread2);
tk_thread_destroy(thread3);
tk_thread_destroy(thread4);
log_debug("application_exit\n");
return RET_OK;
}

View File

@ -3,6 +3,7 @@
* 删除了 assets\_manager\_ref 函数的空字符串的警告信息(感谢智明提供补丁)。
* 修复 vgcanvas\_create 函数返回值的注释(感谢兆坤提供补丁)。
* 修改 sdl release mouse capture 的时机,缓解 Ubuntun 调试时鼠标僵死的问题(感谢商 YUN 涛提供建议)。
* 增加函数 tk\_run\_in\_ui\_thread让后台线程在 UI 线程执行指定的函数,它是对 idle\_queue 的包装,支持等待调用完成。
* 2020/09/28
* 完善自定义控件文档(感谢朝泽提供补丁)。

View File

@ -1,48 +1,118 @@
## 如何在非 GUI 线程操作 GUI 控件
GUI 控件只能在 GUI 线程进行操作,非 GUI 线程想操作 GUI 控件,必须用 idle\_queue 或 timer\_queue 进行串行化。
GUI 控件只能在 GUI 线程进行操作,非 GUI 线程想操作 GUI 控件,必须用以下函数进行串行化。
* idle\_queue 向主循环的事件队列提交一个增加 idle 的请求GUI 线程的主循环在处理事件队列时,会把该 idle 函数放到 idle 管理器中,在分发 idle 时,该 idle 函数在 GUI 线程执行。
### 1. idle\_queue
* timer\_queue 向主循环的事件队列提交一个增加 timer 的请求GUI 线程的主循环在处理事件队列时,会把该 timer 函数放到 timer 管理器中,在分发 timer 时,该 timer 函数在 GUI 线程执行。
idle\_queue 向主循环的事件队列提交一个增加 idle 的请求GUI 线程的主循环在处理事件队列时,会把该 idle 函数放到 idle 管理器中,在分发 idle 时,该 idle 函数在 GUI 线程执行。
**注意idle\_queue 和 timer\_queue 是少数几个可以在非 GUI 线程安全调用的函数。**
```c
/**
* @method idle_queue
* 用于非 GUI 线程增加一个 idle本函数向主循环的事件队列中发送一个增加 idle 的请求。
* @annotation ["static"]
* @param {idle_func_t} on_idle idle 回调函数。
* @param {void*} ctx idle 回调函数的上下文。
*
* @return {ret_t} 返回 RET_OK 表示成功,否则表示失败。
*/
ret_t idle_queue(idle_func_t on_idle, void* ctx);
```
> on\_idle 函数返回 RET\_REPEAT 时,将重复执行。
### 2. timer\_queue
timer\_queue 向主循环的事件队列提交一个增加 timer 的请求GUI 线程的主循环在处理事件队列时,会把该 timer 函数放到 timer 管理器中,在分发 timer 时,该 timer 函数在 GUI 线程执行。
```c
/**
* @method timer_queue
* 用于非 GUI 线程增加一个 timer本函数向主循环的事件队列中发送一个增加 timer 的请求。
* @annotation ["static"]
* @param {timer_func_t} on_timer
* timer 回调函数,回调函数返回 RET_REPEAT则下次继续执行否则自动移出。
* @param {void*} ctx timer 回调函数的上下文。
* @param {uint32_t} duration 时间。
*
* @return {ret_t} 返回 RET_OK 表示成功,否则表示失败。
*/
ret_t timer_queue(timer_func_t on_timer, void* ctx, uint32_t duration);
```
> on\_timer 函数返回 RET\_REPEAT 时,将重复执行。
### 3. tk\_run\_in\_ui\_thread
tk\_run\_in\_ui\_thread 让后台线程在 UI 线程执行指定的函数,它是对 idle\_queue 的包装,支持等待调用完成。
```c
/**
* @method tk_run_in_ui_thread
* 后台线程在 UI 线程执行指定的函数。
*
* @param {tk_callback_t} func 函数。
* @param {void*} ctx 回调函数的上下文。
* @param {bool_t} wait_until_done 是否等待完成。
*
* @return {ret_t} 返回 RET_OK 表示成功,否则表示失败。
*/
ret_t tk_run_in_ui_thread(tk_callback_t func, void* ctx, bool_t wait_until_done)
```
> 如果 wait\_until\_done 为 FALSEfunc 函数返回 RET\_REPEAT 时,将重复执行。
> 注意:以上是少数几个可以在非 GUI 线程安全调用的函数,请不要在非 GUI 线程调用其它 widget 相关的函数。
### 示例:
```
static ret_t on_timer(const timer_info_t* timer) {
return update_progress_bar(WIDGET(timer->ctx));
}
static ret_t on_idle(const idle_info_t* idle) {
return update_progress_bar(WIDGET(idle->ctx));
}
void* thread_entry(void* args) {
int nr = 500;
while(nr-- > 0) {
idle_queue(on_idle, args);
timer_queue(on_timer, args, 30);
```c
void* test_thread1(void* args) {
int nr = 500000;
while ((nr-- > 0) && (!s_app_quit)) {
tk_run_in_ui_thread((tk_callback_t)update_progress_bar, args, TRUE);
sleep_ms(30);
}
return NULL;
}
ret_t application_init() {
thread_t* thread = NULL;
widget_t* progress_bar = NULL;
widget_t* win = window_create(NULL, 0, 0, 0, 0);
widget_t* label = label_create(win, 10, 10, 300, 20);
void* test_thread2(void* args) {
int nr = 500000;
while ((nr-- > 0) && (!s_app_quit)) {
tk_run_in_ui_thread((tk_callback_t)update_progress_bar, args, FALSE);
sleep_ms(30);
}
widget_set_text(label, L"Update progressbar in non GUI thread");
progress_bar = progress_bar_create(win, 10, 80, 300, 20);
return NULL;
}
thread = thread_create(thread_entry, progress_bar);
thread_start(thread);
static ret_t on_idle(const idle_info_t* idle) {
return update_progress_bar(WIDGET(idle->ctx));
}
return RET_OK;
void* test_thread3(void* args) {
int nr = 500000;
while ((nr-- > 0) && (!s_app_quit)) {
idle_queue(on_idle, args);
sleep_ms(30);
}
return NULL;
}
static ret_t on_timer(const timer_info_t* timer) {
return update_progress_bar(WIDGET(timer->ctx));
}
void* test_thread4(void* args) {
int nr = 500000;
while ((nr-- > 0) && (!s_app_quit)) {
timer_queue(on_timer, args, 30);
sleep_ms(30);
}
return NULL;
}
```
@ -52,4 +122,5 @@ ret_t application_init() {
### 注意事项
在 idle 函数执行的时候,窗口可能已经被关闭,控件已经处于无效状态。为了避免出现也指针的问题,在 idle 函数中,应该检查目标窗口和控件是否存在。
* 在 idle 函数执行的时候,窗口可能已经被关闭,控件已经处于无效状态。为了避免出现也指针的问题,在 idle 函数中,应该检查目标窗口和控件是否存在。

View File

@ -23,6 +23,7 @@
#include "tkc/mem.h"
#include "base/idle.h"
#include "base/timer.h"
#include "tkc/thread.h"
#include "tkc/time_now.h"
#include "base/locale_info.h"
#include "tkc/platform.h"
@ -86,6 +87,8 @@
#include "image_loader_web.h"
#endif /*AWTK_WEB*/
static uint64_t s_ui_thread_id = 0;
static ret_t tk_add_font(const asset_info_t* res) {
if (res->subtype == ASSET_TYPE_FONT_BMP) {
#ifdef WITH_BITMAP_FONT
@ -145,6 +148,7 @@ static ret_t awtk_mem_on_out_of_memory(void* ctx, uint32_t tried_times, uint32_t
ret_t tk_init_internal(void) {
font_loader_t* font_loader = NULL;
s_ui_thread_id = tk_thread_self();
#ifdef WITH_DATA_READER_WRITER
data_writer_factory_set(data_writer_factory_create());
data_reader_factory_set(data_reader_factory_create());
@ -362,3 +366,83 @@ int32_t tk_get_pointer_y(void) {
bool_t tk_is_pointer_pressed(void) {
return window_manager_get_pointer_pressed(window_manager());
}
bool_t tk_is_ui_thread(void) {
return s_ui_thread_id == tk_thread_self();
}
typedef struct _idle_callback_info_t {
void* ctx;
bool_t done;
bool_t sync;
ret_t result;
tk_callback_t func;
} idle_callback_info_t;
static idle_callback_info_t* idle_callback_info_create(tk_callback_t func, void* ctx) {
idle_callback_info_t* info = TKMEM_ZALLOC(idle_callback_info_t);
return_value_if_fail(info != NULL, NULL);
info->func = func;
info->ctx = ctx;
return info;
}
static ret_t idle_callback_info_destroy(idle_callback_info_t* info) {
return_value_if_fail(info != NULL && info->func != NULL, RET_BAD_PARAMS);
memset(info, 0x00, sizeof(*info));
TKMEM_FREE(info);
return RET_OK;
}
static ret_t idle_func_of_callback(const idle_info_t* info) {
idle_callback_info_t* cinfo = (idle_callback_info_t*)(info->ctx);
ret_t ret = cinfo->func(cinfo->ctx);
if (cinfo->sync) {
cinfo->done = TRUE;
cinfo->result = ret;
/*call by sync not allow repeat*/
assert(ret != RET_REPEAT);
return RET_REMOVE;
} else {
if (ret != RET_REPEAT) {
idle_callback_info_destroy(cinfo);
ret = RET_REMOVE;
}
return ret;
}
}
ret_t tk_run_in_ui_thread(tk_callback_t func, void* ctx, bool_t wait_until_done) {
return_value_if_fail(func != NULL, RET_BAD_PARAMS);
if (tk_is_ui_thread()) {
return func(ctx);
} else {
idle_callback_info_t* info = idle_callback_info_create(func, ctx);
return_value_if_fail(info != NULL, RET_OOM);
info->sync = wait_until_done;
if (idle_queue(idle_func_of_callback, info) == RET_OK) {
ret_t ret = RET_OK;
if (wait_until_done) {
while (!(info->done)) {
sleep_ms(20);
}
ret = info->result;
idle_callback_info_destroy(info);
}
return ret;
} else {
idle_callback_info_destroy(info);
return RET_FAIL;
}
}
}

View File

@ -140,6 +140,26 @@ ret_t tk_deinit_internal(void);
*/
ret_t tk_exit(void);
/**
* @method tk_is_ui_thread
*
* 线UI线程
* @return {bool_t} TRUE表示是
*/
bool_t tk_is_ui_thread(void);
/**
* @method tk_run_in_ui_thread
* 线UI线程执行指定的函数
*
* @param {tk_callback_t} func
* @param {void*} ctx
* @param {bool_t} wait_until_done
*
* @return {ret_t} RET_OK表示成功
*/
ret_t tk_run_in_ui_thread(tk_callback_t func, void* ctx, bool_t wait_until_done);
END_C_DECLS
#endif /*TK_GLOBAL_H*/

View File

@ -321,6 +321,7 @@ typedef bool_t (*tk_is_valid_t)(void* data);
typedef bool_t (*tk_filter_t)(void* ctx, const void* data);
typedef int (*tk_compare_t)(const void* a, const void* b);
typedef ret_t (*tk_visit_t)(void* ctx, const void* data);
typedef ret_t (*tk_callback_t)(void* ctx);
/*TK_NAME_LEN+1 must aligned to 4*/
enum { TK_NAME_LEN = 31 };