tprogress_

文中鼠标焦点泛指接收输入设备的事件,输入设备包括鼠标、键盘。

  • 阻塞式任务是必须立即执行的任务。静默认任务是可以延缓执行的任务,它必须放在定时器中。
  • 由于静默任务的hidden_ms内不处理SDL事件、不调用定时器,hidden_ms不能设得太长,最大2秒。
  • 系统任一时刻最多存在一个任务。

有这么一个人员系统。首先发送“init”请求向服务器得到已有人员信息,然后每隔30秒左右发送“keepalive”向服务器获得人员库的最新版本,要是发现有变动了,发送“sync”下载人员变化部分,然合并到本地人员库。

  • 阻塞式任务。到某个逻辑时,任务必须立即执行,只有执行出来结果后,逻辑才能继续。对执行时间,任务可能快很执行完毕,像不到一秒,也可能较长时间。当时间较长时,希望在界面显示个进度条。示例中的“init”、“sync”属于阻塞式任务。
  • 静默任务。到某个逻辑时,可以执行任务了,但认为不满意的话,可以不立即执行,可以等稍后下一个时刻。对执行时间,和阻塞式任务一样,任务可能快很执行完毕,像不到一秒,也可能较长时间。当时间较长时,希望在界面显示个进度条。示例中“keepalive”属于静默认务。定时器超过30秒,可以发送了,如果不想,也可等到下一个定时器时刻,像30.01秒。

不论阻塞式任务还是静默式任务,都在主线程执行。在其它线程执行的任务称为后台任务、工作者任务,实现上用net::tworker。

类型api接管焦点情况要求环境
阻塞式任务run_with_progress一旦运行,就要接管鼠标焦点。虽然hidden_ms时隐藏着进度条,那也要接管(注1)无限制
静默任务run_with_progress_quiet一开始hidden_ms毫秒不接收SDL事件,hidden_ms毫秒后才接管焦点(注2)timer例程内(注3)
  • 注1:阻塞式任务中hidden_ms作用是指示在开始的hidden_ms毫秒内,不显示进度条。举个例子,要去服务器获取人员列表,hidden_ms设了3000,那获取操作如果在3秒内完成,界面不会显示进度条。假设没有hidden_ms参数,那只要操作一开始就有进度条,结果是一闪而过,这会让界面不友好。注意,阻塞式任务全程接管鼠标焦点,这和hidden_ms是什么值无关。
  • 注2:静默任务中hidden_ms参考下面的“静默任务的hidden_ms毫秒”。
  • 注4:要求静默认任务必须在timer内执行,是避免出一些意外。来本要求静默认任务的场合应该不多,而且是可以延时的,放在timer是个较好选择。

一、静默任务的hidden_ms毫秒

要完美的话,希望在这段时间里,不会影响界面处理,和平时一样接收并处理鼠标、键盘事件。想法很好,实际上很难做到。

图1 chromium-http中MessagePumpForIO禁止嵌套
  • 静默任务极可能是chromium-http,像上面发“keepalive”。chromium-http不允许嵌套,也就是说,在执行过程中禁止产生新的chromium-http。于是会遇到无法处理的情况:在执行chromium-http静默任务,界面那边遇到要求立即产生新有chromium-http请求了。原因见图1,state_->run_depth只能是1,禁止嵌套。
  • 虽然都是在主线程,静默任务执行的操作和界面产生操作是否真的能保证互不影响?

目前采用的是在这段时间内,1)不调用events::pump(),即不从SDL取走事件、不调用定时器,2)不刷新界面。

因为不取走SDL事件,这样hidden_ms内执行完的话,鼠标依旧可以保持着之前的跟随等状态,不会因为新来静默任务而中止拖拽。因为不调用定时器、刷新界面,界面会静止,像显示摄像头图像时。因为不能让压着太多SDL事件,以及界面长时间不动,设的hidden_ms不能太长,目前这个时间最长2秒。

void tprogress_widget::show_slice()
{
	timer_handler(true);
	bool allow_handle_event = !quiet || float_track_.is_visible();
	if (allow_handle_event) {
		twindow::tunique_event_widget_lock lock(window_, *float_track_.widget.get());
		twidget::tdisable_popup_window_lock lock2("Must not popup new window during progress.show_slice().");
		events::pump();

		if (!float_track_.is_visible()) {
			absolute_draw();
		} else {
			// why not use absolute_draw()?
			// --twindow::draw will call ttrack::did_draw_, did_draw_ may run_with_progress_widget or user's app logic,
			//   and result dead-loop or exception.
			absolute_draw_float_widgets();
		}
	}

	if (::instance != nullptr) {
		::instance->app_ros_slice();
	}
	// Add a delay so we don't keep spinning if there's no event.
	SDL_Delay(10);

	if (!require_cancel_ && ::instance->terminating()) {
		cancel_task();

	} else if (float_track_.is_visible() && (last_frameTexture_size_.!= gui2::settings::screen_width || last_frameTexture_size_.!= gui2::settings::screen_height)) {
		set_track_rect();
	}
}

allow_handle_event要满足false,只一种情况,即正处在静默任务的hidden_ms毫秒。可以看到,在这时,不执行tunique_event_widget_lock(接管焦点)、events::pump(处理SDL事件、定时器),absolute_draw(刷新界面),但会调用::instance->app_window_slice()。

二、先instance->pump,后gui2::pump_timers

namespace events {
void pump()
{
  ...
  if (!progress_running) {
    instance->pump(false);
    gui2::pump_timers();
  }
  ...
}
}

为什么先执行instance->pump,然后是pump_timers。如果调换了,让看下面这个操作序列。

  1. 在这两语句外面的处理SDL事件过程中,产生了一个app_OnMessage方式的阻塞式任务。
  2. (gui2::pump_timers())判断出要新执行一个静默任务,由于当前没有任务,开始执行这个静默任务。
  3. (静默认任执行中,instance->pump)处理app_OnMessage投递来的阻塞式任务,但由于有静默任务正在执行,导致系统无法处理。

改为先instance->pump,然后pump_timers。

  1. 在这两语句外面的处理SDL事件过程中,产生了一个app_OnMessage方式的阻塞式任务。
  2. (instance->pump)处理app_OnMessage投递来的阻塞式任务。
  3. (阻塞式任务执行中,gui2::pump_timers())判断出要新执行一个静默任务,但由于有阻塞式任务正在执行,于是此次定时时刻不执行静默任务,等将来某个定时时刻阻塞式任务结束了,再执行这个静默任务。

全部评论: 0

    写评论: