chromium源碼學(xué)習(xí)——線程池(下)

從base/task_scheduler/task_traits.h中的枚舉量TaskShutdownBehavior可以看到罚舱,chromium針對投遞的task在瀏覽器退出時(shí)應(yīng)該表現(xiàn)的行為分為三類,CONTINUE_ON_SHUTDOWN皮迟、SKIP_ON_SHUTDOWN、BLOCK_SHUTDOWN存炮。值得一提的是base::SequencedWorkerPool::WorkerShutdown中同樣聲明了三個(gè)一樣的enum玻孟,一般我們在開發(fā)過程中應(yīng)該盡量避免這種同樣的常量在不同的位置定義兩次的行為,因?yàn)楹芸赡艽a在不同的人手上改著改著這兩份東西就不一樣了锤躁,這樣就埋了個(gè)深坑。如果實(shí)在萬不得已的情況下只能定義兩份,那就學(xué)習(xí)一下chromium的做法吧,用一個(gè)static_assert保證兩份定義的值是一樣的:

bool SequencedWorkerPool::Inner::PostTaskToTaskScheduler(...) {
  ...
  static_assert(
      static_cast<int>(TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN) ==
      static_cast<int>(CONTINUE_ON_SHUTDOWN),
      "TaskShutdownBehavior and WorkerShutdown enum mismatch for "
      "CONTINUE_ON_SHUTDOWN.");
  ...

然后先看一下這三種分類到底是幾個(gè)意思玩敏,其實(shí)代碼里已經(jīng)有了非常詳細(xì)的注釋。簡單來說大概就是這樣:

  1. CONTINUE_ON_SHUTDOWN類型的任務(wù)不會(huì)影響退出流程觉啊,它跟退出流程各走各的拣宏,所以可能任務(wù)執(zhí)行的時(shí)候退出流程已經(jīng)走得七七八八了沈贝,所以很多單例啊全局對象啊之類的已經(jīng)析構(gòu)了,這個(gè)要比較注意勋乾;
  2. SKIP_ON_SHUTDOWN類型的任務(wù)宋下,在開始退出流程前已經(jīng)在執(zhí)行的會(huì)繼續(xù)執(zhí)行且執(zhí)行時(shí)阻塞退出流程,否則會(huì)被忽略辑莫;
  3. BLOCK_SHUTDOWN類型的任務(wù)学歧,不論在退出流程開始前后投遞都會(huì)被執(zhí)行,且執(zhí)行時(shí)阻塞退出流程各吨。換句話說就是這些任務(wù)執(zhí)行完前退出流程會(huì)等在那里枝笨。
    那么這個(gè)又是怎么實(shí)現(xiàn)的呢?
    首先揭蜒,在向線程池投遞的每個(gè)任務(wù)都會(huì)由TaskTracker來跟蹤横浑,在正式加入線程池的任務(wù)隊(duì)列之前會(huì)經(jīng)過TaskTracker::WillPostTask,如果返回false則不會(huì)被加入隊(duì)列(是否已經(jīng)開始退出流程是在TaskTracker::State里面屉更,在一個(gè)int值的最低位存的徙融,估計(jì)是跟任務(wù)數(shù)量合在一個(gè)變量里面存有助于用原子操作優(yōu)化吧):
bool TaskTracker::WillPostTask(const Task* task) {
  if (!BeforePostTask(task->traits.shutdown_behavior()))
    return false;
  ……


bool TaskTracker::BeforePostTask(TaskShutdownBehavior shutdown_behavior) {
  if (shutdown_behavior == TaskShutdownBehavior::BLOCK_SHUTDOWN) {
    // BLOCK_SHUTDOWN tasks block shutdown between the moment they are posted
    // and the moment they complete their execution.
    const bool shutdown_started = state_->IncrementNumTasksBlockingShutdown();

    if (shutdown_started) {
      AutoSchedulerLock auto_lock(shutdown_lock_);

      // A BLOCK_SHUTDOWN task posted after shutdown has completed is an
      // ordering bug. This aims to catch those early.
      DCHECK(shutdown_event_);
      if (shutdown_event_->IsSignaled()) {
        ……
        return false;
      }
      ……
     }
     return true;
  }

  // A non BLOCK_SHUTDOWN task is allowed to be posted iff shutdown hasn't
  // started.
  return !state_->HasShutdownStarted();
}

可以看出,在開始退出流程之后瑰谜,除了BLOCK_SHUTDOWN類型的任務(wù)就不會(huì)再被加入任務(wù)隊(duì)列了欺冀;
同樣,在開始執(zhí)行任務(wù)之前也有類似的判斷萨脑,這里BeforeRunTask里的代碼就不貼了:

bool TaskTracker::RunTask(std::unique_ptr<Task> task,
                          const SequenceToken& sequence_token) {
  DCHECK(task);
  DCHECK(sequence_token.IsValid());

  const TaskShutdownBehavior shutdown_behavior =
      task->traits.shutdown_behavior();
  const bool can_run_task = BeforeRunTask(shutdown_behavior);
  const bool is_delayed = !task->delayed_run_time.is_null();

  if (can_run_task) {
    PerformRunTask(std::move(task), sequence_token);
    AfterRunTask(shutdown_behavior);
  }
  ...

最后隐轩,退出流程是在哪里進(jìn)行的呢?始于TaskTracker::PerformShutdown渤早,由于某些任務(wù)會(huì)阻塞退出流程龙助,在它們執(zhí)行完成之前需要等待,所以這里用了一個(gè)Event對象進(jìn)行同步:

void TaskTracker::PerformShutdown() {
  {
    AutoSchedulerLock auto_lock(shutdown_lock_);

    shutdown_event_.reset(
      new WaitableEvent(WaitableEvent::ResetPolicy::MANUAL,
                      WaitableEvent::InitialState::NOT_SIGNALED));

    const bool tasks_are_blocking_shutdown = state_->StartShutdown();
    if (!tasks_are_blocking_shutdown) {
      shutdown_event_->Signal();
      return;
    }
  }

  // It is safe to access |shutdown_event_| without holding |lock_| because the
  // pointer never changes after being set above.
  {
    base::ThreadRestrictions::ScopedAllowWait allow_wait;
    shutdown_event_->Wait();
  }
  ...
}

TaskTracker::State里用一個(gè)int值的最低位存放是否已經(jīng)開始退出流程,其他位存放阻塞類型任務(wù)的數(shù)量(對這個(gè)計(jì)數(shù)的維護(hù)可以在TaskTracker中查找IncrementNumTasksBlockingShutdown與DecrementNumTasksBlockingShutdown的調(diào)用)提鸟,在TaskTracker::StartShutdown里面會(huì)將退出狀態(tài)標(biāo)志位置位军援,并判斷目前是否有阻塞類型任務(wù)。如果沒有称勋,則可以直接結(jié)束退出流程胸哥;否則需要等待這些任務(wù)完成∩南剩可以看一下StartShutdown里面的實(shí)現(xiàn):

class TaskTracker::State {
  // Sets a flag indicating that shutdown has started. Returns true if there are
  // tasks blocking shutdown. Can only be called once.
  bool StartShutdown() {
    const auto new_value =
        subtle::NoBarrier_AtomicIncrement(&bits_, kShutdownHasStartedMask);

    // Check that the "shutdown has started" bit isn't zero. This would happen
    // if it was incremented twice.
    DCHECK(new_value & kShutdownHasStartedMask);

    const auto num_tasks_blocking_shutdown =
        new_value >> kNumTasksBlockingShutdownBitOffset;
    return num_tasks_blocking_shutdown != 0;
  }
  …
}

那么如果需要等待其他任務(wù)完成的話空厌,最終這些任務(wù)執(zhí)行完后會(huì)在哪里重新喚醒退出流程呢?每個(gè)任務(wù)在執(zhí)行前會(huì)經(jīng)過TaskTracker::BeforeRunTask银酬,執(zhí)行完成后會(huì)經(jīng)過TaskTracker::AfterRunTask嘲更,在這兩個(gè)過程中都會(huì)對阻塞類型任務(wù)的數(shù)量與是否已經(jīng)開始退出流程進(jìn)行判斷,如果判斷成立則會(huì)喚醒原來的退出流程繼續(xù)執(zhí)行揩瞪,以AfterRunTask為例:

void TaskTracker::AfterRunTask(TaskShutdownBehavior shutdown_behavior) {
  if (shutdown_behavior == TaskShutdownBehavior::BLOCK_SHUTDOWN ||
      shutdown_behavior == TaskShutdownBehavior::SKIP_ON_SHUTDOWN) {
    const bool shutdown_started_and_no_tasks_block_shutdown =
        state_->DecrementNumTasksBlockingShutdown();
    if (shutdown_started_and_no_tasks_block_shutdown)
      OnBlockingShutdownTasksComplete();
  }
}

void TaskTracker::OnBlockingShutdownTasksComplete() {
  AutoSchedulerLock auto_lock(shutdown_lock_);

  // This method can only be called after shutdown has started.
  DCHECK(state_->HasShutdownStarted());
  DCHECK(shutdown_event_);

  shutdown_event_->Signal();
}
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末赋朦,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子李破,更是在濱河造成了極大的恐慌宠哄,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,602評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件嗤攻,死亡現(xiàn)場離奇詭異毛嫉,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)妇菱,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,442評論 2 382
  • 文/潘曉璐 我一進(jìn)店門承粤,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人闯团,你說我怎么就攤上這事辛臊。” “怎么了偷俭?”我有些...
    開封第一講書人閱讀 152,878評論 0 344
  • 文/不壞的土叔 我叫張陵浪讳,是天一觀的道長。 經(jīng)常有香客問我涌萤,道長淹遵,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,306評論 1 279
  • 正文 為了忘掉前任负溪,我火速辦了婚禮透揣,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘川抡。我一直安慰自己辐真,他們只是感情好须尚,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,330評論 5 373
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著侍咱,像睡著了一般耐床。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上楔脯,一...
    開封第一講書人閱讀 49,071評論 1 285
  • 那天撩轰,我揣著相機(jī)與錄音,去河邊找鬼昧廷。 笑死堪嫂,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的木柬。 我是一名探鬼主播皆串,決...
    沈念sama閱讀 38,382評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼眉枕!你這毒婦竟也來了恶复?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,006評論 0 259
  • 序言:老撾萬榮一對情侶失蹤齐遵,失蹤者是張志新(化名)和其女友劉穎寂玲,沒想到半個(gè)月后塔插,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體梗摇,經(jīng)...
    沈念sama閱讀 43,512評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,965評論 2 325
  • 正文 我和宋清朗相戀三年想许,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了伶授。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,094評論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡流纹,死狀恐怖糜烹,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情漱凝,我是刑警寧澤疮蹦,帶...
    沈念sama閱讀 33,732評論 4 323
  • 正文 年R本政府宣布,位于F島的核電站茸炒,受9級特大地震影響愕乎,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜壁公,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,283評論 3 307
  • 文/蒙蒙 一感论、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧紊册,春花似錦比肄、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,286評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽掀亥。三九已至,卻和暖如春妥色,著一層夾襖步出監(jiān)牢的瞬間铺浇,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,512評論 1 262
  • 我被黑心中介騙來泰國打工垛膝, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留鳍侣,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 45,536評論 2 354
  • 正文 我出身青樓吼拥,卻偏偏與公主長得像倚聚,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子凿可,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,828評論 2 345

推薦閱讀更多精彩內(nèi)容