chromium Callback() and Bind()

1.0、Introduction

base::Callback<>和base::Bind()他们组合可以提供一种类型安全的回调方法。可以绑定一些参数生成一个函数调用。这样就可以传递延迟执行的函数参数了。例如,在chromium代码中用于不同线程上调度任务,或者在不同的MessageLoops上调度任务。
一个回调函数没有绑定任何参数的时候(base::Callback<void()>)被封装成了base::Closure。注意,这里与其他环境有所不同,它不保留对闭包的引用。

1.1 OnceCallback<> And RepeatingCallback<>

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

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

1.2 Memory Management And Passing

对于base::{Once,Repeating}Callback 如果所有权被转移了就传值,否则传引用。

// |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()给这个回调函数传值,否则就直接传递对象。当函数需要独占变量,并且你没有使用std::move(),此时你将会得到compile error。

回调变量使用了Reset()函数就会变成空,通过id_null()来判断回调的变量是否为空。

2.0 Quick reference for basic stuff

2.1 Binding A Bare Function
example 1:
int Return5() { return 5; }
base::OnceCallback<int()> func_cb = base::BindOnce(&Return5);
LOG(INFO) << std::move(func_cb).Run();  // Prints 5.

example 2:
int Return5() { return 5; }
base::RepeatingCallback<int()> func_cb = base::BindRepeating(&Return5);
LOG(INFO) << func_cb.Run();  // Prints 5.
2.2 Binding A Captureless Lambda
base::Callback<int()> lambda_cb = base::Bind([] { return 4; });
LOG(INFO) << lambda_cb.Run();  // Print 4.

base::OnceCallback<int()> lambda_cb2 = base::BindOnce([] { return 3; });
LOG(INFO) << std::move(lambda_cb2).Run();  // Print 3.
2.3 Binding A Class Method

第一个参数是函数,第二个参数是对应方法的实例

class Ref : public base::RefCountedThreadSafe<Ref> {
 public:
  int Foo() { return 3; }
};
scoped_refptr<Ref> ref = new Ref();
base::Callback<void()> ref_cb = base::Bind(&Ref::Foo, ref);
LOG(INFO) << ref_cb.Run();  // Prints out 3.

默认情况下,对象必须支持引用计数,否则你将会得到一个compile error。如果你是在线程间使用,必须要保证引用计数线程安全。如果你不想使用引用计数请见下文的“Advanced binding of member function”

2.4 Running A Callback

回调函数Callback可以调用Run()函数,他具有和回调函数一样的参数。注意base::OnceCallback::Run() 是使用回调对象,并且只能使用他的右值进行调用。

void DoSomething(const base::Callback<void(int, std::string)>& callback) {
  callback.Run(5, "hello");
}

void DoSomethingOther(base::OnceCallback<void(int, std::string)> callback) {
  std::move(callback).Run(5, "hello");
}

RepeatingCallbacks 可以运行多次。除了使用base::Passed,参见下文。

void DoSomething(const base::RepeatingCallback<double(double)>& callback) {
  double myresult = callback.Run(3.14159);
  myresult += callback.Run(2.71828);
}

注意RepeatingCallbacks有下面这个问题,但是OnceCallback没有,因为OnceCallback永远都是使用std::move来执行Run()函数:

如果运行回调函数导致自身的销毁,那么需要再回调之前,先std::move,如下:

void Foo::RunCallback() {
  std::move(&foo_deleter_callback_).Run();
}
2.5 Creating a Callback That Does Nothing

有时候你出发回调但是不需要做任何事情(test code 或者 只是触发一个事件),此时就可以传一个默认构造的相同类型过去

using MyCallback = base::OnceCallback<void(bool arg)>;
void MyFunction(MyCallback callback) {
  std::move(callback).Run(true);  // Uh oh...
}
...
MyFunction(MyCallback());  // ...this will crash when Run()!

默认构造Callback为空,所以不能使用Run(),可以使用base::DoNothing()

MyFunction(base::DoNothing());  // Can be Run(), will no-op

base::DoNothing() 可以为任何返回void的OnceCallback和RepeatingCallback来使用。如果尝试给bind参数,这个将导致base::DoNothing()失效。
通常,将参数绑定到DoNothing()的惟一原因是管理对象的生存期,在这种情况下,应该尽量使用DeleteSoon()、ReleaseSoon()或RefCountedDeleteOnSequence之类的习惯用法。
如果你真的需要绑定一个参数到DoNothing(),或者你需要显式地创建一个回调对象(因为通过操作符()()的隐式转换不会编译),你可以直接实例化:

// Binds |foo_ptr| to a no-op OnceCallback takes a scoped_refptr<Foo>.
// ANTIPATTERN WARNING: This should likely be changed to ReleaseSoon()!
base::Bind(base::DoNothing::Once<scoped_refptr<Foo>>(), foo_ptr);
2.6 Passing Input Parameters

Passing Unbound Input Parameters

注意绑定参数和不绑定参数的区别

void MyFunc(int i, const std::string& str) {}
base::Callback<void(int, const std::string&)> cb = base::Bind(&MyFunc);
cb.Run(23, "hello, world");

Passing Bound Input Parameters

void MyFunc(int i, const std::string& str) {}
base::Callback<void()> cb = base::Bind(&MyFunc, 23, "hello world");
cb.Run();

一个回调没有绑定参数的时候(base::Callback<void()>)称为 base::Closure,有下面的使用方式:

base::Closure cb = base::Bind(&MyFunc, 23, "hello world");
或者
base::Closure cb = base::Bind(&MyClass::MyFunc, this, 23, "hello world");
2.7 Partial Binding Of Parameters (Currying)

当使用Callback的时候可以指定部分参数,在执行回调的时候再指定剩下的参数。

void ReadIntFromFile(const std::string& filename,
                     base::OnceCallback<void(int)> on_read);

void DisplayIntWithPrefix(const std::string& prefix, int result) {
  LOG(INFO) << prefix << result;
}

void AnotherFunc(const std::string& file) {
  ReadIntFromFile(file, base::BindOnce(&DisplayIntWithPrefix, "MyPrefix: "));
};

这个技术叫做curry,这个应该使用它来代替包含绑定参数的适配器类。注意上面代码有一个参数"MyPrefix: "为const char *,但是对应的bind是const std::string&,这里是进行了类型转换。

2.8 Avoiding Copies With Callback Parameters

避免使用回调参数的副本。base::BindOnce() 一般使用移动构造。

std::vector<int> v = {1, 2, 3};
// |v| is moved into the internal storage without copy.
base::Bind(&Foo, std::move(v));


// The vector is moved into the internal storage without copy.
base::Bind(&Foo, std::vector<int>({1, 2, 3}));

相反的,base::BindRepeating()只有在绑定了base::Passed()的情况下才能使用std::move移动目标。不建议使用base::BindRepeating()和base::Passed()来达到base::BindOnce()的效果,直接使用base::BindOnce()更好。避免同时使用base::Passed()和base::BindOnce(),因为std::move()已经做了相同的事情了。

void Foo(std::unique_ptr<int>) {}
auto p = std::make_unique<int>(42);

// |p| is moved into the internal storage of Bind(), and moved out to |Foo|.
base::BindOnce(&Foo, std::move(p));
base::BindRepeating(&Foo, base::Passed(&p)); // Ok, but subtle.
base::BindRepeating(&Foo, base::Passed(std::move(p))); // Ok, but subtle.

3.0 Quick reference for advanced binding

3.1 Binding A Class Method With Weak Pointers

如果一个类里面有成员base::WeakPtr weak_this_,可以这样使用

base::Bind(&MyClass::Foo, weak_this_);

如果对象被销毁了callback就不能被Run()了。
注意,绑定到base::WeakPtrs的类方法回调只能在销毁对象的同一序列上运行,因为否则回调的执行可能与对象的删除竞争。

要使用base::WeakPtr和base::Bind(), MyClass通常看起来像:

class MyClass {
public:
  MyClass() {
    weak_this_ = weak_factory_.GetWeakPtr();
  }
private:
  base::WeakPtr<MyClass> weak_this_;
  // MyClass member variables go here.
  base::WeakPtrFactory<MyClass> weak_factory_{this};
};

weak_factory_是MyClass中的最后一个成员变量,因此首先销毁它。这如果任何绑定到weak_this_的类方法在销毁期间调用Run(),那么它们实际上不会被执行。
如果MyClass只调用base::Bind()相同的执行回调,那么在调用base::Bind()时调用weak_factory_.GetWeakPtr()通常是安全的,而不是在构造期间使用单独的weak_this_。

3.2 Binding A Class Method With Manual Lifetime Management
base::Bind(&MyClass::Foo, base::Unretained(this));

这样就禁止了这个对象的所有的生命周期的管理,你有必要知道你调用的过程中这个对象一直存在。

3.3 Binding A Class Method And Having The Callback Own The Class
MyClass* myclass = new MyClass;
base::Bind(&MyClass::Foo, base::Owned(myclass));

这个对象在callback被销毁的时候删除,即使没有调用Run()成功,就像是在关机的时候发送了一个任务到线程池一样。
智能指针也支持这样子的情况

std::unique_ptr<MyClass> myclass(new MyClass);
base::Bind(&MyClass::Foo, std::move(myclass));
3.4 Ignoring Return Values

有时候你调用一个callback,但是不需要返回的值,可以这样使用

int DoSomething(int arg) { cout << arg << endl; }
base::Callback<void(int)> cb =
    base::Bind(IgnoreResult(&DoSomething));

4.0 Quick reference for binding parameters to Bind()

传递参数给函数是通过用base::Bind()。一个callback没有任何参数的时候叫做base::Closure(),他和base::Callback<void()>是一样的东西。

4.1 Passing Parameters Owned By The Callback
void Foo(int* arg) { cout << *arg << endl; }
int* pn = new int(1);
base::Closure foo_callback = base::Bind(&foo, base::Owned(pn));

当callback被销毁的时候,即使没有run,参数也会被删除的。

4.2 Passing Parameters As A unique_ptr
void TakesOwnership(std::unique_ptr<Foo> arg) {}
auto f = std::make_unique<Foo>();
// f becomes null during the following call.
base::OnceClosure cb = base::BindOnce(&TakesOwnership, std::move(f));

参数的所有权是跟随callback的,会将所有权传递给回调函数的。所以这个只能运行一次,并且如果回调函数没有被运行,它还是会在callback被销毁的时候删除掉的。

4.3 Passing Parameters As A scoped_refptr
void TakesOneRef(scoped_refptr<Foo> arg) {}
scoped_refptr<Foo> f(new Foo);
base::Closure cb = base::Bind(&TakesOneRef, f);

This should “just work.” 这个Closure在它的生命周期内会一直持有它的引用,

void DontTakeRef(Foo* arg) {}
scoped_refptr<Foo> f(new Foo);
base::Closure cb = base::Bind(&DontTakeRef, base::RetainedRef(f));

base::RetainedRed在callback执行Run()的时候回持有对象的引用,并且传递一个原始指针( a raw pointer)给对象。

4.4 Passing Parameters By Reference

引用将会被copy,除非使用std::ref 和 std::cref,Example:

void foo(const int& arg) { printf("%d %p\n", arg, &arg); }
int n = 1;
base::Closure has_copy = base::Bind(&foo, n);
base::Closure has_ref = base::Bind(&foo, std::cref(n));
n = 2;
foo(n);                        // Prints "2 0xaaaaaaaaaaaa"
has_copy.Run();                // Prints "1 0xbbbbbbbbbbbb"
has_ref.Run();                 // Prints "2 0xaaaaaaaaaaaa"

一般来讲,在Closure中参数都是在copy。值得注意的是:std::ref and std::cref存储的是一个reference or const reference. 这个意味着你需要保证在callback之外他们依然在生命周期之内。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值