Chromium Threading and Task

本文代码基于101 在Chromium中,不仅有大名鼎鼎的多进程架构,还有自己分工明确的多线程架构。在Chromium的多线程架构中,是以Task作为调度的基本单位。

Chromium 多线程设计思想

  • The main goal is to keep the main thread (a.k.a. “UI” thread in the browser process) and IO thread (each process’ thread for handling IPC) responsive. This means offloading any blocking I/O or other expensive operations to other threads. (主要目的是为了让主线程(例如 Browser 进程中的 UI 线程)和 IO 线程(进程中处理 IPC 消息的线程)保持快速响应。这意味着要把任何I/O阻塞和其他昂贵的操作,放在其他线程里面执行。可以放在是 devicated thread 或者 threadpool )

  • Locks are also an option to synchronize access but in Chrome we strongly prefer sequences to locks.(锁是一种实现同步访问的选择,但是在 Chrome 里面,我们更喜欢使用 sequences,而不是锁)

  • Most threads have a loop that gets tasks from a queue and runs them (the queue may be shared between multiple threads).(大量的线程有一个可以从队列里面获取任务并执行它们的 loop )

线程的类型

在一个进程中,往往有如下几种线程:

  • 一个 main thread

    • 在 Browser 进程中 (BrowserThread::UI):用于更新 UI

    • 在 Render 进程中:运行Blink

  • 一个 io thread

    • 在 Browser 进程中(BrowserThread::IO): 用于处理 IPC 消息以及网络请求

    • 在 Render 进程中:用于处理IPC消息

  • 一些使用 base::Tread 创建的,有特殊用途的线程(可能存在)

  • 一些在使用线程池时产生的线程(可能存在)

注意,在 Chromium,我们应该是用 base::Thread 来创建线程,而不是每个平台下的特定的 platform thread

Task

task 封装了一个 base::OnceCloseure。我们会通过 TaskRunner 把 task 放进一个 TaskQueue 里面,以至于能一直执行相对应的方法。

Task 的执行方式

头文件:https://source.chromium.org/chromium/chromium/src/+/main:base/task/task_executor.h;l=6?q=task_executor.h&sq=

TaskRunner

  • Task 会以并行、无序地执行,可能同一时间内在任意线程上执行所有任务(通过线程池实现 base::TaskRunner)

  • 下面的代码通过TaskRunner PostTask 两个任务,它们会并行执行

#include <iostream>
#include <thread>
#include <unistd.h>

#include "base/run_loop.h"
#include "base/command_line.h" 
#include "base/task/single_thread_task_executor.h"
#include "base/task/task_runner.h"
#include "base/task/sequenced_task_runner.h"
#include "base/task/thread_pool.h"
#include "base/task/thread_pool/thread_pool_instance.h"
#include "base/task/task_executor.h"

void doWork0(){
    std::cout << "doWork0,tid=" << std::this_thread::get_id ()  << std::endl;
    sleep(10);
    std::cout << "doWork0, finish" <<  std::endl;
}

void doWork1(){
    std::cout << "doWork1,tid=" << std::this_thread::get_id ()  << std::endl;
}

int main(int argc, const char* argv[])  {
    base::CommandLine::Init(argc,argv);
    base::SingleThreadTaskExecutor main_task_executor(base::MessagePumpType::UI);
    base::ThreadPoolInstance::CreateAndStartWithDefaultParams("Demo");
    std::cout << "hello test task----------\n";
    std::cout << "main,tid=" << std::this_thread::get_id ()  << std::endl;

    //1 test SequencedTaskRunner
    // scoped_refptr<base::SequencedTaskRunner> task_runner =
    //                 base::ThreadPool::CreateSequencedTaskRunner(
    //                     {base::MayBlock(), base::TaskPriority::USER_VISIBLE,
    //                      base::TaskShutdownBehavior::BLOCK_SHUTDOWN});

    // task_runner->PostTask(FROM_HERE, base::BindOnce(&doWork0));
    // task_runner->PostTask(FROM_HERE, base::BindOnce(&doWork1));

    scoped_refptr<base::TaskRunner> task_runner = base::ThreadPool::CreateTaskRunner({base::TaskPriority::USER_VISIBLE});
    task_runner->PostTask(FROM_HERE, base::BindOnce(&doWork0));
    task_runner->PostTask(FROM_HERE, base::BindOnce(&doWork1));

    base::RunLoop loop;
    loop.Run();
    return 0;    
}

SequencedTaskRunner

  • Task 会以加入的顺序执行,同一时间内在任意线程上执行一个任务(SequencedTaskRunner 实现)。Chrome 不鼓励使用锁。 序列本质上提供线程安全。 更喜欢始终从相同序列访问的类,而不是使用锁来管理您自己的线程安全。线程安全但不是线程仿射; 怎么会这样? 发布到同一序列的任务将按顺序运行。 在一个已排序的任务完成后,下一个任务可能会被另一个工作线程拾取,但该任务可以保证看到前一个任务对其序列造成的任何副作用。

  • 下面的代码通过SequencedTaskRunner PostTask 两个任务,它们会顺序执行,其中doWork0睡眠了10s,doWork1需要等到doWork0执行完后执行

#include <iostream>
#include <thread>
#include <unistd.h>

#include "base/run_loop.h"
#include "base/command_line.h" 
#include "base/task/single_thread_task_executor.h"
#include "base/task/sequenced_task_runner.h"
#include "base/task/thread_pool.h"
#include "base/task/thread_pool/thread_pool_instance.h"

void doWork0(){
    std::cout << "doWork0,tid=" << std::this_thread::get_id ()  << std::endl;
    sleep(10);
    std::cout << "doWork0, finish" <<  std::endl;
}

void doWork1(){
    std::cout << "doWork1,tid=" << std::this_thread::get_id ()  << std::endl;
}

int main(int argc, const char* argv[])  {
    base::CommandLine::Init(argc,argv);
    base::SingleThreadTaskExecutor main_task_executor(base::MessagePumpType::UI);
    base::ThreadPoolInstance::CreateAndStartWithDefaultParams("Demo");

    scoped_refptr<base::SequencedTaskRunner> task_runner =
                    base::ThreadPool::CreateSequencedTaskRunner(
                        {base::MayBlock(), base::TaskPriority::USER_VISIBLE,
                         base::TaskShutdownBehavior::BLOCK_SHUTDOWN});

    std::cout << "hello test task----------\n";
    std::cout << "main,tid=" << std::this_thread::get_id ()  << std::endl;
    task_runner->PostTask(FROM_HERE, base::BindOnce(&doWork0));
    task_runner->PostTask(FROM_HERE, base::BindOnce(&doWork1));

    base::RunLoop loop;
    loop.Run();
    return 0;    
}

SingleSuqenceTaskRunner

  • Task 会以加入的顺序执行,同一时间内只在固定的线程上执行一个任务(通过 SingleSuqenceTaskRunner 实现)

如果多个任务需要在同一个线程上运行,并且该线程不必是主线程或 IO 线程,请将它们发布到由 创建的 SingleThreadTaskRunner。

#include <iostream>
#include <thread>
#include <unistd.h>

#include "base/run_loop.h"
#include "base/command_line.h" 
#include "base/task/single_thread_task_executor.h"
#include "base/task/task_runner.h"
#include "base/task/sequenced_task_runner.h"
#include "base/task/thread_pool.h"
#include "base/task/thread_pool/thread_pool_instance.h"
#include "base/task/task_executor.h"

void doWork0(){
    std::cout << "doWork0,tid=" << std::this_thread::get_id ()  << std::endl;
    sleep(10);
    std::cout << "doWork0, finish" <<  std::endl;
}

void doWork1(){
    std::cout << "doWork1,tid=" << std::this_thread::get_id ()  << std::endl;
}

int main(int argc, const char* argv[])  {
    base::CommandLine::Init(argc,argv);
    base::SingleThreadTaskExecutor main_task_executor(base::MessagePumpType::UI);
    base::ThreadPoolInstance::CreateAndStartWithDefaultParams("Demo");
    std::cout << "hello test task----------\n";
    std::cout << "main,tid=" << std::this_thread::get_id ()  << std::endl;

    //1 test SequencedTaskRunner
    // scoped_refptr<base::TaskRunner> task_runner = base::ThreadPool::CreateTaskRunner({base::TaskPriority::USER_VISIBLE});
    // task_runner->PostTask(FROM_HERE, base::BindOnce(&doWork0));
    // task_runner->PostTask(FROM_HERE, base::BindOnce(&doWork1));
    
    //2 test SequencedTaskRunner
    // scoped_refptr<base::SequencedTaskRunner> task_runner =
    //                 base::ThreadPool::CreateSequencedTaskRunner(
    //                     {base::MayBlock(), base::TaskPriority::USER_VISIBLE,
    //                      base::TaskShutdownBehavior::BLOCK_SHUTDOWN});

    // task_runner->PostTask(FROM_HERE, base::BindOnce(&doWork0));
    // task_runner->PostTask(FROM_HERE, base::BindOnce(&doWork1));

    //3 test SingleThreadTaskRunner
    scoped_refptr<base::SingleThreadTaskRunner> task_runner =
                    base::ThreadPool::CreateSingleThreadTaskRunner(
                        {base::MayBlock(), base::TaskPriority::USER_VISIBLE,
                         base::TaskShutdownBehavior::BLOCK_SHUTDOWN});

    task_runner->PostTask(FROM_HERE, base::BindOnce(&doWork0));
    task_runner->PostTask(FROM_HERE, base::BindOnce(&doWork1));
    base::RunLoop loop;
    loop.Run();
    return 0;    
}

Posting a Task

https://source.chromium.org/chromium/chromium/src/+/main:base/task/task_runner.h;bpv=1;bpt=1

在TaskRunner类中定义了PostTask的方式

  // Equivalent to PostDelayedTask(from_here, task, 0).
  bool PostTask(const Location& from_here, OnceClosure task);
  
  // Like PostTask, but tries to run the posted task only after |delay_ms|
  // has passed. Implementations should use a tick clock, rather than wall-
  // clock time, to implement |delay|.
  virtual bool PostDelayedTask(const Location& from_here,
                               OnceClosure task,
                               base::TimeDelta delay) = 0;
                               
  bool PostTaskAndReply(const Location& from_here,
                        OnceClosure task,
                        OnceClosure reply);
                        
   // Templating on the types of `task` and `reply` allows template matching to
  // work for both base::RepeatingCallback and base::OnceCallback in each case.
  template <typename TaskReturnType,
            typename ReplyArgType,
            template <typename>
            class TaskCallbackType,
            template <typename>
            class ReplyCallbackType,
            typename = EnableIfIsBaseCallback<TaskCallbackType>,
            typename = EnableIfIsBaseCallback<ReplyCallbackType>>
  bool PostTaskAndReplyWithResult(const Location& from_here,
                                  TaskCallbackType<TaskReturnType()> task,
                                  ReplyCallbackType<void(ReplyArgType)> reply) {
    DCHECK(task);
    DCHECK(reply);
    // std::unique_ptr used to avoid the need of a default constructor.
    auto* result = new std::unique_ptr<TaskReturnType>();
    return PostTaskAndReply(
        from_here,
        BindOnce(&internal::ReturnAsParamAdapter<TaskReturnType>,
                 std::move(task), result),
        BindOnce(&internal::ReplyAdapter<TaskReturnType, ReplyArgType>,
                 std::move(reply), Owned(result)));
  }

Posting to the Current Thread

// The task will run on the current thread in the future.
base::ThreadTaskRunnerHandle::Get()->PostTask(
    FROM_HERE, base::BindOnce(&Task));
    
// The task will run on the current (virtual) thread's default task queue.
base::SequencedTaskRunnerHandle::Get()->PostTask(
    FROM_HERE, base::BindOnce(&Task);
#include <iostream>
#include <thread>
#include <unistd.h>

#include "base/run_loop.h"
#include "base/command_line.h" 
#include "base/task/single_thread_task_executor.h"
#include "base/task/task_runner.h"
#include "base/task/sequenced_task_runner.h"
#include "base/task/thread_pool.h"
#include "base/task/thread_pool/thread_pool_instance.h"
#include "base/task/task_executor.h"
#include "base/threading/sequenced_task_runner_handle.h"
#include "base/threading/thread_task_runner_handle.h"

void doWork0(){
    std::cout << "doWork0,tid=" << std::this_thread::get_id ()  << std::endl;
    sleep(1);
    std::cout << "doWork0, finish" <<  std::endl;
}

void doWork1(){
    std::cout << "doWork1,tid=" << std::this_thread::get_id ()  << std::endl;
}

void doWork2(){
    std::cout << "doWork2,tid=" << std::this_thread::get_id ()  << std::endl;
    sleep(2);
    std::cout << "doWork2, finish" <<  std::endl;
}

void doWork3(){
    std::cout << "doWork3,tid=" << std::this_thread::get_id ()  << std::endl;
}

int main(int argc, const char* argv[])  {
    base::CommandLine::Init(argc,argv);
    base::SingleThreadTaskExecutor main_task_executor(base::MessagePumpType::UI);
    base::ThreadPoolInstance::CreateAndStartWithDefaultParams("Demo");
    std::cout << "hello test task----------\n";
    std::cout << "main,tid=" << std::this_thread::get_id ()  << std::endl;


    base::SequencedTaskRunnerHandle::Get()->PostTask(FROM_HERE, base::BindOnce(&doWork0));
    base::SequencedTaskRunnerHandle::Get()->PostTask(FROM_HERE, base::BindOnce(&doWork1));

    base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, base::BindOnce(&doWork2));
    base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, base::BindOnce(&doWork3));

    base::RunLoop loop;
    loop.Run();
    return 0;    
}

两种方式都在主线程执行

注意,在使用 base::BindOnce() 方法产生一个 base::OnceClosure 的时候,一般会传递一个 base::WeakPrt,而不是一个裸指针。base::WeakPrt能确保所指向的对象销毁时,绑定在对象上的回调能被取消。否则一般会产生一个段错误

OnceCallback<> and BindOnce(), RepeatingCallback<> and BindRepeating()

定义:https://source.chromium.org/chromium/chromium/src/+/main:base/bind.h;l=6;drc=d5c694fc357d3b1cb40b3d40bb0c1e1ca15d952f?q=bind.h&sq=&ss=chromium%2Fchromium%2Fsrc

base::Callback<>和base::Bind()他们组合可以提供一种类型安全的回调方法。可以绑定一些参数生成一个函数调用。这样就可以传递延迟执行的函数参数了。例如,在chromium代码中用于不同线程上调度任务,或者在不同的MessageLoops上调度任务。

一个回调函数没有绑定任何参数的时候(base::Callback<void()>)被封装成了base::Closure。注意,这里与其他环境有所不同,它不保留对闭包的引用。

OnceCallback

OnceCallback<>由base::BindOnce()创建,这个回到函数变量是一个仅移动类型并且只能运行一次。默认情况下,这会将绑定参数从内部存储转移到绑定函数上去,因此更加容易跟可移动类型一起使用。回调函数有一个很好的特性是:声明周期非常明确,所以更加容易判断线程之间的调用何时被销毁。

oncecallback的实现:base/callback.h

emplate <typename R, typename... Args>
class OnceCallback<R(Args...)> : public internal::CallbackBase {
 public:
  using ResultType = R;
  using RunType = R(Args...);
  using PolymorphicInvoke = R (*)(internal::BindStateBase*,
                                  internal::PassingType<Args>...);

  constexpr OnceCallback() = default;
  OnceCallback(std::nullptr_t) = delete;

  constexpr OnceCallback(internal::NullCallbackTag) : OnceCallback() {}
  constexpr OnceCallback& operator=(internal::NullCallbackTag) {
    *this = OnceCallback();
    return *this;
  }

  constexpr OnceCallback(internal::NullCallbackTag::WithSignature<RunType>)
      : OnceCallback(internal::NullCallbackTag()) {}
  constexpr OnceCallback& operator=(
      internal::NullCallbackTag::WithSignature<RunType>) {
    *this = internal::NullCallbackTag();
    return *this;
  }

  constexpr OnceCallback(internal::DoNothingCallbackTag)
      : OnceCallback(BindOnce([](Args... args) {})) {}
  constexpr OnceCallback& operator=(internal::DoNothingCallbackTag) {
    *this = BindOnce([](Args... args) {});
    return *this;
  }

  constexpr OnceCallback(internal::DoNothingCallbackTag::WithSignature<RunType>)
      : OnceCallback(internal::DoNothingCallbackTag()) {}
  constexpr OnceCallback& operator=(
      internal::DoNothingCallbackTag::WithSignature<RunType>) {
    *this = internal::DoNothingCallbackTag();
    return *this;
  }

  explicit OnceCallback(internal::BindStateBase* bind_state)
      : internal::CallbackBase(bind_state) {}

  OnceCallback(const OnceCallback&) = delete;
  OnceCallback& operator=(const OnceCallback&) = delete;

  OnceCallback(OnceCallback&&) noexcept = default;
  OnceCallback& operator=(OnceCallback&&) noexcept = default;

  OnceCallback(RepeatingCallback<RunType> other)
      : internal::CallbackBase(std::move(other)) {}

  OnceCallback& operator=(RepeatingCallback<RunType> other) {
    static_cast<internal::CallbackBase&>(*this) = std::move(other);
    return *this;
  }

  R Run(Args... args) const & {
    static_assert(!sizeof(*this),
                  "OnceCallback::Run() may only be invoked on a non-const "
                  "rvalue, i.e. std::move(callback).Run().");
    NOTREACHED();
  }

  R Run(Args... args) && {
    // Move the callback instance into a local variable before the invocation,
    // that ensures the internal state is cleared after the invocation.
    // It's not safe to touch |this| after the invocation, since running the
    // bound function may destroy |this|.
    OnceCallback cb = std::move(*this);
    PolymorphicInvoke f =
        reinterpret_cast<PolymorphicInvoke>(cb.polymorphic_invoke());
    return f(cb.bind_state_.get(), std::forward<Args>(args)...);
  }

  // Then() returns a new OnceCallback that receives the same arguments as
  // |this|, and with the return type of |then|. The returned callback will:
  // 1) Run the functor currently bound to |this| callback.
  // 2) Run the |then| callback with the result from step 1 as its single
  //    argument.
  // 3) Return the value from running the |then| callback.
  //
  // Since this method generates a callback that is a replacement for `this`,
  // `this` will be consumed and reset to a null callback to ensure the
  // originally-bound functor can be run at most once.
  template <typename ThenR, typename... ThenArgs>
  OnceCallback<ThenR(Args...)> Then(OnceCallback<ThenR(ThenArgs...)> then) && {
    CHECK(then);
    return BindOnce(
        internal::ThenHelper<
            OnceCallback, OnceCallback<ThenR(ThenArgs...)>>::CreateTrampoline(),
        std::move(*this), std::move(then));
  }

  // This overload is required; even though RepeatingCallback is implicitly
  // convertible to OnceCallback, that conversion will not used when matching
  // for template argument deduction.
  template <typename ThenR, typename... ThenArgs>
  OnceCallback<ThenR(Args...)> Then(
      RepeatingCallback<ThenR(ThenArgs...)> then) && {
    CHECK(then);
    return BindOnce(
        internal::ThenHelper<
            OnceCallback,
            RepeatingCallback<ThenR(ThenArgs...)>>::CreateTrampoline(),
        std::move(*this), std::move(then));
  }
};

重要的接口:

/*

OnceCallback::Run() 只能在非常量右值,即 std::move(callback).Run()
*/ 
 R Run(Args... args) const & {
    static_assert(!sizeof(*this),
                  "OnceCallback::Run() may only be invoked on a non-const "
                  "rvalue, i.e. std::move(callback).Run().");
    NOTREACHED();
  }
/*
在调用之前将回调实例移动到局部变量中,
 确保调用后内部状态被清除。
触摸 |this| 不安全 调用之后,因为运行
绑定函数可能会破坏 |this|。
*/
  R Run(Args... args) && {
    // Move the callback instance into a local variable before the invocation,
    // that ensures the internal state is cleared after the invocation.
    // It's not safe to touch |this| after the invocation, since running the
    // bound function may destroy |this|.
    OnceCallback cb = std::move(*this);
    PolymorphicInvoke f =
        reinterpret_cast<PolymorphicInvoke>(cb.polymorphic_invoke());
    return f(cb.bind_state_.get(), std::forward<Args>(args)...);
  }

OnceCallback 禁用了左值接口Run,强制调用右值接口Run,在右值接口Run中,将自己移动到局部变量,因此仅仅能使用一次

#include <iostream>
#include <thread>
#include <unistd.h>
#include <memory>

#include "base/run_loop.h"
#include "base/command_line.h" 
#include "base/task/single_thread_task_executor.h"
#include "base/task/task_runner.h"
#include "base/task/sequenced_task_runner.h"
#include "base/task/thread_pool.h"
#include "base/task/thread_pool/thread_pool_instance.h"
#include "base/task/task_executor.h"
#include "base/threading/sequenced_task_runner_handle.h"
#include "base/threading/thread_task_runner_handle.h"
#include "base/threading/thread.h"



class Test{
public:
  Test() = default;
  ~Test() = default;

  // 左值调用的接口
  int  test()  &{
    std::cout << " &  \n";
     return 1;
  }

  // 右值调用的接口
  int  test()  &&{
    std::cout << " &&  \n";
     return 1;
  }

};


void testOnceCallback(){

  std::cout << "testOnceCallback \n" << std::endl;
}

int main(int argc, const char* argv[])  {
    base::CommandLine::Init(argc,argv);
    base::SingleThreadTaskExecutor main_task_executor(base::MessagePumpType::UI);
    base::ThreadPoolInstance::CreateAndStartWithDefaultParams("Demo");
    std::cout << "hello test task----------\n";
    std::cout << "main,tid=" << std::this_thread::get_id ()  << std::endl;

    Test t;
    t.test();
    std::move(t).test();
    base::RunLoop loop;
    loop.Run();
    return 0;    
}

示例:

#include <iostream>
#include <thread>
#include <unistd.h>
#include <memory>

#include "base/run_loop.h"
#include "base/command_line.h" 
#include "base/task/single_thread_task_executor.h"
#include "base/task/task_runner.h"
#include "base/task/sequenced_task_runner.h"
#include "base/task/thread_pool.h"
#include "base/task/thread_pool/thread_pool_instance.h"
#include "base/task/task_executor.h"
#include "base/threading/sequenced_task_runner_handle.h"
#include "base/threading/thread_task_runner_handle.h"
#include "base/threading/thread.h"




void testOnceCallback(){

  std::cout << "testOnceCallback \n" << std::endl;
}

int main(int argc, const char* argv[])  {
    base::CommandLine::Init(argc,argv);
    base::SingleThreadTaskExecutor main_task_executor(base::MessagePumpType::UI);
    base::ThreadPoolInstance::CreateAndStartWithDefaultParams("Demo");
    std::cout << "hello test task----------\n";
    std::cout << "main,tid=" << std::this_thread::get_id ()  << std::endl;


    base::OnceCallback<void()> cb =  base::BindOnce(&testOnceCallback);
    //cb.Run();// build error
    std::move(cb).Run();

    std::cout << (cb?"cb":"cb is nullptr") << std::endl;
    std::move(cb).Run();// cb is nullptr  run will  crash !

    base::RunLoop loop;
    loop.Run();
    return 0;    
}

注意:base::OnceCallback<void()> cb 不允许 的使用方式:cb.Run(); 这样将编译错误!

在调用std::move(cb).Run();后cb变成nullptr,之后就不能再调用:

 

base::RepeatingCallback

base::RepeatingCallback<> 由 base::BindRepeating()创建,显然这个是可以反复调用的,所以也很难判断是否被销毁状态等问题。

源码:


template <typename R, typename... Args>
class RepeatingCallback<R(Args...)> : public internal::CallbackBaseCopyable {
 public:
  using ResultType = R;
  using RunType = R(Args...);
  using PolymorphicInvoke = R (*)(internal::BindStateBase*,
                                  internal::PassingType<Args>...);

  constexpr RepeatingCallback() = default;
  RepeatingCallback(std::nullptr_t) = delete;

  constexpr RepeatingCallback(internal::NullCallbackTag)
      : RepeatingCallback() {}
  constexpr RepeatingCallback& operator=(internal::NullCallbackTag) {
    *this = RepeatingCallback();
    return *this;
  }

  constexpr RepeatingCallback(internal::NullCallbackTag::WithSignature<RunType>)
      : RepeatingCallback(internal::NullCallbackTag()) {}
  constexpr RepeatingCallback& operator=(
      internal::NullCallbackTag::WithSignature<RunType>) {
    *this = internal::NullCallbackTag();
    return *this;
  }

  constexpr RepeatingCallback(internal::DoNothingCallbackTag)
      : RepeatingCallback(BindRepeating([](Args... args) {})) {}
  constexpr RepeatingCallback& operator=(internal::DoNothingCallbackTag) {
    *this = BindRepeating([](Args... args) {});
    return *this;
  }

  constexpr RepeatingCallback(
      internal::DoNothingCallbackTag::WithSignature<RunType>)
      : RepeatingCallback(internal::DoNothingCallbackTag()) {}
  constexpr RepeatingCallback& operator=(
      internal::DoNothingCallbackTag::WithSignature<RunType>) {
    *this = internal::DoNothingCallbackTag();
    return *this;
  }

  explicit RepeatingCallback(internal::BindStateBase* bind_state)
      : internal::CallbackBaseCopyable(bind_state) {}

  // Copyable and movable.
  RepeatingCallback(const RepeatingCallback&) = default;
  RepeatingCallback& operator=(const RepeatingCallback&) = default;
  RepeatingCallback(RepeatingCallback&&) noexcept = default;
  RepeatingCallback& operator=(RepeatingCallback&&) noexcept = default;

  bool operator==(const RepeatingCallback& other) const {
    return EqualsInternal(other);
  }

  bool operator!=(const RepeatingCallback& other) const {
    return !operator==(other);
  }

  R Run(Args... args) const & {
    PolymorphicInvoke f =
        reinterpret_cast<PolymorphicInvoke>(this->polymorphic_invoke());
    return f(this->bind_state_.get(), std::forward<Args>(args)...);
  }

  R Run(Args... args) && {
    // Move the callback instance into a local variable before the invocation,
    // that ensures the internal state is cleared after the invocation.
    // It's not safe to touch |this| after the invocation, since running the
    // bound function may destroy |this|.
    RepeatingCallback cb = std::move(*this);
    PolymorphicInvoke f =
        reinterpret_cast<PolymorphicInvoke>(cb.polymorphic_invoke());
    return f(std::move(cb).bind_state_.get(), std::forward<Args>(args)...);
  }

  // Then() returns a new RepeatingCallback that receives the same arguments as
  // |this|, and with the return type of |then|. The
  // returned callback will:
  // 1) Run the functor currently bound to |this| callback.
  // 2) Run the |then| callback with the result from step 1 as its single
  //    argument.
  // 3) Return the value from running the |then| callback.
  //
  // If called on an rvalue (e.g. std::move(cb).Then(...)), this method
  // generates a callback that is a replacement for `this`. Therefore, `this`
  // will be consumed and reset to a null callback to ensure the
  // originally-bound functor will be run at most once.
  template <typename ThenR, typename... ThenArgs>
  RepeatingCallback<ThenR(Args...)> Then(
      RepeatingCallback<ThenR(ThenArgs...)> then) const& {
    CHECK(then);
    return BindRepeating(
        internal::ThenHelper<
            RepeatingCallback,
            RepeatingCallback<ThenR(ThenArgs...)>>::CreateTrampoline(),
        *this, std::move(then));
  }

  template <typename ThenR, typename... ThenArgs>
  RepeatingCallback<ThenR(Args...)> Then(
      RepeatingCallback<ThenR(ThenArgs...)> then) && {
    CHECK(then);
    return BindRepeating(
        internal::ThenHelper<
            RepeatingCallback,
            RepeatingCallback<ThenR(ThenArgs...)>>::CreateTrampoline(),
        std::move(*this), std::move(then));
  }
};

重要的接口:

  R Run(Args... args) const & {
    PolymorphicInvoke f =
        reinterpret_cast<PolymorphicInvoke>(this->polymorphic_invoke());
    return f(this->bind_state_.get(), std::forward<Args>(args)...);
  }

  R Run(Args... args) && {
    // Move the callback instance into a local variable before the invocation,
    // that ensures the internal state is cleared after the invocation.
    // It's not safe to touch |this| after the invocation, since running the
    // bound function may destroy |this|.
    RepeatingCallback cb = std::move(*this);
    PolymorphicInvoke f =
        reinterpret_cast<PolymorphicInvoke>(cb.polymorphic_invoke());
    return f(std::move(cb).bind_state_.get(), std::forward<Args>(args)...);
  }

#include <iostream>
#include <thread>
#include <unistd.h>
#include <memory>

#include "base/run_loop.h"
#include "base/command_line.h" 
#include "base/task/single_thread_task_executor.h"
#include "base/task/task_runner.h"
#include "base/task/sequenced_task_runner.h"
#include "base/task/thread_pool.h"
#include "base/task/thread_pool/thread_pool_instance.h"
#include "base/task/task_executor.h"
#include "base/threading/sequenced_task_runner_handle.h"
#include "base/threading/thread_task_runner_handle.h"
#include "base/threading/thread.h"




void testOnceCallback(){

  std::cout << "testOnceCallback \n" << std::endl;
}

int main(int argc, const char* argv[])  {
    base::CommandLine::Init(argc,argv);
    base::SingleThreadTaskExecutor main_task_executor(base::MessagePumpType::UI);
    base::ThreadPoolInstance::CreateAndStartWithDefaultParams("Demo");
    std::cout << "hello test task----------\n";
    std::cout << "main,tid=" << std::this_thread::get_id ()  << std::endl;

    base::RepeatingCallback<void()> cb =  base::BindRepeating(&testOnceCallback);
    cb.Run();
    cb.Run();
    base::RunLoop loop;
    loop.Run();
    return 0;    
}

 

// |Foo| just refers to |cb| but doesn't store it nor consume it.
bool Foo(const base::OnceCallback<void(int)>& cb) {
  return cb.is_null();
}

// |Bar| takes the ownership of |cb| and stores |cb| into |g_cb|.
base::RepeatingCallback<void(int)> g_cb;
void Bar(base::RepeatingCallback<void(int)> cb) {
  g_cb = std::move(cb);
}

// |Baz| takes the ownership of |cb| and consumes |cb| by Run().
void Baz(base::OnceCallback<void(int)> cb) {
  std::move(cb).Run(42);
}

// |Qux| takes the ownership of |cb| and transfers ownership to PostTask(),
// which also takes the ownership of |cb|.
void Qux(base::RepeatingCallback<void(int)> cb) {
  PostTask(FROM_HERE, base::BindOnce(cb, 42));
  PostTask(FROM_HERE, base::BindOnce(std::move(cb), 43));
}

 

如果不需要保存引用就使用std::move()给这个回调函数传值,否则就直接传递对象。

Binding A Class Method

#include <iostream>
#include <thread>
#include <unistd.h>

#include "base/run_loop.h"
#include "base/command_line.h" 
#include "base/task/single_thread_task_executor.h"
#include "base/task/task_runner.h"
#include "base/task/sequenced_task_runner.h"
#include "base/task/thread_pool.h"
#include "base/task/thread_pool/thread_pool_instance.h"
#include "base/task/task_executor.h"
#include "base/threading/sequenced_task_runner_handle.h"
#include "base/threading/thread_task_runner_handle.h"

class Ref : public base::RefCountedThreadSafe<Ref> {
 public:

    Ref(){}
    Ref(const Ref&) = delete;
    Ref& operator=(const Ref&) = delete;

    void Foo(int num) { std::cout << num << "-Foo\n"; }

  private:
    friend class RefCountedThreadSafe<Ref>;
    ~Ref();
};
Ref::~Ref(){

    std::cout << "Ref::~Ref()\n";
}

void test(){
    scoped_refptr<Ref> ref = new Ref();
    base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, base::BindOnce(&Ref::Foo, ref,1));
    
    base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(FROM_HERE, base::BindOnce(&Ref::Foo, ref,2),base::Milliseconds(1000));

}

int main(int argc, const char* argv[])  {
    base::CommandLine::Init(argc,argv);
    base::SingleThreadTaskExecutor main_task_executor(base::MessagePumpType::UI);
    base::ThreadPoolInstance::CreateAndStartWithDefaultParams("Demo");
    std::cout << "hello test task----------\n";
    std::cout << "main,tid=" << std::this_thread::get_id ()  << std::endl;

    test();
    base::RunLoop loop;
    loop.Run();
    return 0;    
}

RefCountedThreadSafe和scoped_refptr的使用 https://blog.51cto.com/xiamachao/2461171

当PostDelayedTask执行完后,ref的引用为0,d调用析构函数。

如果在test函数中使用裸指针,Ref* ref = new Ref();如下图,则会造成内存泄漏

void test1(){
    Ref *ref = new Ref();
    base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, base::BindOnce(&Ref::Foo, base::Unretained(ref),1));
    
    base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(FROM_HERE, base::BindOnce(&Ref::Foo, base::Unretained(ref),2),base::Milliseconds(1000));
}

base::Unretained的意思是Task不保证对象的存在,由发布任务的调用方,保证回调执行时,this指针仍然可用。 在使用base::Unretained构建Task时需要谨慎处理,因为它不会增加对象的引用计数,因此必须确保Task执行完之前对象不会销毁,否则容易在base::Callback::Run里面引起崩溃(但不一定能在调试时碰到)。 如果不能确保对象在Task之后销毁,最好把对象从base::RefCountedThreadSafe继承,在base::Bind时使用裸指针或者scoped_refptr。如果是发给当前线程的Task可以使用WeakPtr。

 PostTaskAndReplyWithResult

#include <iostream>
#include <thread>
#include <unistd.h>

#include "base/run_loop.h"
#include "base/command_line.h" 
#include "base/task/single_thread_task_executor.h"
#include "base/task/task_runner.h"
#include "base/task/sequenced_task_runner.h"
#include "base/task/thread_pool.h"
#include "base/task/thread_pool/thread_pool_instance.h"
#include "base/task/task_executor.h"
#include "base/threading/sequenced_task_runner_handle.h"
#include "base/threading/thread_task_runner_handle.h"

class Ref : public base::RefCountedThreadSafe<Ref> {
 public:

    Ref(){}
    Ref(const Ref&) = delete;
    Ref& operator=(const Ref&) = delete;

    void Foo(int num) { std::cout << num << "-Foo\n"; }
   
    int add(int left,int right){
        std::cout << "set:" <<   left  << "+" << right << "\n";
        sleep(10);  
        return left + right;
    }

    void set(int v){
        std::cout << "set:" <<   v << "\n";     
    }
  private:
    friend class RefCountedThreadSafe<Ref>;
    ~Ref();
};
Ref::~Ref(){

    std::cout << "Ref::~Ref()\n";
}

void test2(){
    /*
    / PostTaskAndReplyWithResult的含义
			// 参数1是执行代码的位置
			// 参数2是一个闭包A, 有一个返回值(入参不限)  如int add(int left,int right)
			// 参数3是一个闭包B, 有多个入参, 没有返回值  如  void set(int v)
			// 闭包A绑定时, 要将入参都填好
			// 闭包B绑定时, 要将前面的入参都填好,最后一个入参由闭包A的执行结果来给定
			// 执行效果:
			// 先投递任务到任务队列
			// 任务队列取出任务执行
			// 先执行闭包A, 得到返回结果A1
			// 再执行闭包B, 将A1作为闭包B的最后一个入参
			// 闭包B执行完成
			// 任务执行完成
    */
    scoped_refptr<Ref> ref = new Ref();
    base::ThreadTaskRunnerHandle::Get()->PostTaskAndReplyWithResult(FROM_HERE,
                        base::BindOnce(&Ref::add,ref,1,2),
                        base::BindOnce(&Ref::set,ref));

}

int main(int argc, const char* argv[])  {
    base::CommandLine::Init(argc,argv);
    base::SingleThreadTaskExecutor main_task_executor(base::MessagePumpType::UI);
    base::ThreadPoolInstance::CreateAndStartWithDefaultParams("Demo");
    std::cout << "hello test task----------\n";
    std::cout << "main,tid=" << std::this_thread::get_id ()  << std::endl;

    test2();
    base::RunLoop loop;
    loop.Run();
    return 0;    
}

底层原理

从上述的代码可以看出,我们可以通过各种 TaskRunner 的子类来 post task。那么task 是怎么被调度,运行的呢?下图是具体的类图。

MessageLoop

整个消息循环的入口,会初始化 MessagePumpSequenceManagerTaskQueue 等对象,并调用其BindToCurrentThread方法。一般 base::Thread 都会有一个 MessageLoopCurrent 实例。但因为因为主线程并不是由 base::Thread 类型,所以必须手动声明一个 MessageLoop

MessagePump

顾名思义,MessagePump是消息泵,循环地处理与其所关联的消息(承担着消息循环的功能)。经典的消息循环如下:

for (;;) {
  bool did_work = DoInternalWork();
  if (should_quit_)
    break;

  did_work |= delegate_->DoWork();
  if (should_quit_)
    break;

  TimeTicks next_time;
  did_work |= delegate_->DoDelayedWork(&next_time);
  if (should_quit_)
    break;

  if (did_work)
    continue;

  did_work = delegate_->DoIdleWork();
  if (should_quit_)
    break;

  if (did_work)
    continue;

  WaitForWork();
}

ThreadController

获取要被调度的 task,并传递给TaskAnnotator。 ThreadController::SechuleWork() 方法会从 SequencdTaskSource::SelectNextTask中获取下一个要被调动的 task,并调用 TaskAnnotator::RunTask()方法执行该 task

SequencedTaskSource

ThreadControll 提供要被调度的 task,这个 task 来自于 TaskQueueSelector::SelectWorkQueueToService()

TaskQueueSelector

TaskQueueSelector 会从当前的 TaskQueue 中的 immediate_work_queuedelayed_incoming_queue 中获取相对应的 task

TaskQueue

Task 保存在 TaskQueue 里面, TaskQueue的具体实现里面,有四个队列:

  • Immediate (non-delayed) tasks:

    • immediate_incoming_queue - PostTask enqueues tasks here.

    • immediate_work_queue - SequenceManager takes immediate tasks here.

  • Delayed tasks

    • delayed_incoming_queue - PostDelayedTask enqueues tasks here.

    • delayed_work_queue - SequenceManager takes delayed tasks here.

immediate_work_queue 为空的时候,immediate_work_queue 会和 immediate_incoming_queue 进行交换。delayed task 都会先保存在 delayed_incoming_queue,然后会有个名为TimeDomain类,会把到了时间的 delayed task 加入到 delayed_work_queue中。

上述过程中的流程图表示如下:

参考文档

  • https://chromium.googlesource.com/chromium/src/+/HEAD/docs/callback.md

  • https://chromium.googlesource.com/chromium/src/+/HEAD/docs/threading_and_tasks.md

  • https://blog.csdn.net/Summer__show_/article/details/102807329

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值