chromium学习:thread

  • Post author:
  • Post category:其他



在前面,我们学习了content_shell的流程,串成了一条执行线。从今天开始,我们要开始学习线上的点,由点延伸成为面,最后组成一个学习chromium的网。



首先,我们先介绍chromium中的thread相关知识。




chromium中有哪些线程呢?


1. UI线程。应用程序起来后的主线程。


2. IO线程。负责browser进程和子进程之间的调度线程


3. file线程。不解释


4. db线程。不解释


5.

safe_browsing线程。不清楚


chromium关于thread的

设计原则

有两个:1. 不阻塞UI线程,使得UI有更好的响应。2.不鼓励加锁机制和线程安全对象。






如何做到的呢?那就要考虑chromium中的线程模型了。




1. 不在UI线程作阻塞IO操作,不在IO线程作阻塞IO操作。




2. 线程之间不互相阻塞




3. 许多API都是异步的




为了避免加锁机制,chromium提供的thread模型是在每个线程内保留消息循环,线程之间通过消息传递任务,处理回调函数。



关于多线程的加锁,摘抄如下一段话


多线程编程一直是一件麻烦的事情,线程执行的不确定性,资源的并发访问,一直困扰着众多程序员们。为了解决多线程编程的麻烦,大家想出了很多经典的方案:如:对资源直接加锁,角色模型,CSP,FP等等。他们的思想基本分为两类:一类是对存在并发访问资源直接加锁,第二类是避免资源被并发访问。前者存在许多问题,如死锁,优先级反转等等,而相对来说,后者会好很多,角色模型,CSP和FP就都属于后者,Chrome也是使用后者的思想来实现多线程的。


从开发的角度,我们该如何使用chromium提供的这种线程模型呢?



提供模板



base::Callback<>,该类有一个Run函数。其作为函数指针泛型,由base::Bind产生。废话少说,看个例子就什么都明白了




void ReadToString(const std::string& filename, const base::Callback<void(const std::string&)>& on_read);

void DisplayString(const std::string& result) {
  LOG(INFO) << result;
}

void SomeFunc(const std::string& file) {
  ReadToString(file, base::Bind(&DisplayString));
};

解释:base::Bind把函数指针 &DisplayString 转化为 base::Callback<void(const std::string& result)>。参数部分请参考

柯里化




如何往一个线程里面放任务呢?




chromium提供的线程模型里面提供PostTask,PostDelayedTask方法。我们以PostTask为例。


void MessageLoop::PostTask(
    const tracked_objects::Location& from_here, const base::Closure& task)
其中,
typedef Callback<void(void)> Closure;

因此,可以看到如下一个简单的例子


void WriteToFile(const std::string& filename, const std::string& data);
BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE,
                        base::Bind(&WriteToFile, "foo.txt", "hello world!"));

如何让一个类成员函数作为task呢?




同样的道理,只是第一个参数变为类对象。如下例子,不过注意对象通常是引用计数的。


class MyObject : public base::RefCountedThreadSafe<MyObject> {
 public:
  void DoSomething(const std::string16& name) {
    thread_->message_loop()->PostTask(
       FROM_HERE, base::Bind(&MyObject::DoSomethingOnAnotherThread, <strong>this</strong>, name));
  }

  void DoSomethingOnAnotherThread(const std::string16& name) {
    ...
  }
 private:
  // Always good form to make the destructor private so that only RefCountedThreadSafe can access it.
  // This avoids bugs with double deletes.
  friend class base::RefCountedThreadSafe<MyObject>;

  ~MyObject();
  Thread* thread_;
};

注意:base::bind中的参数会被复制到内部存储结构。



取消回调




有两种情形需要取消回调:




1. 需要在一个对象稍后做一些事情,但是到那时,当你的回调调用的时候,对象可能被删除了




2. 情形改变了,有些老任务为了效率考虑需要取消。


取消的重要声明:


取消一个拥有参数的task是危险的,因为可能造成内存泄漏。参考下例

class MyClass {
 public:
  // Owns |p|.
  void DoSomething(AnotherClass* p) {
    ...
  }
  WeakPtr<MyClass> AsWeakPtr() {
    return weak_factory_.GetWeakPtr();
  }
 private:
  base::WeakPtrFactory<MyObject> weak_factory_;
};

...
Closure cancelable_closure = Bind(&MyClass::DoSomething, object->AsWeakPtr(), p);
Callback<void(AnotherClass*)> cancelable_callback = Bind(&MyClass::DoSomething, object->AsWeakPtr());
...

void FunctionRunLater(const Closure& cancelable_closure,
                      const Callback<void(AnotherClass*)>& cancelable_callback) {
  ...
  // Leak memory!
  cancelable_closure.Run();
  cancelable_callback.Run(p);
}





在FunctionRunLater中,如果object已经被释放的话,两个Run()函数都会造成p泄漏。我们可以使用scoped_ptr来解决这个问题


class MyClass {
 public:
  void DoSomething(scoped_ptr<AnotherClass> p) {
    ...
  }
  ...
};


base::WeakPtr 和取消


我们可以使用base::WeakPtr 和 base::WeakPtrFactory,用于保证在object的生命周期之外对应的调用不会发生,甚至不必使用引用计数。base::Bind为base::WeakPtr提供了一种机制,当base::WeakPtr设置为无效的时候,禁止task的执行。其中base::WeakPtrFactory是用于生成

base::WeakPtr对象的。当factory释放的时候,所有base::WeakPtr都会在其内部设置“无效”位,用于任何绑定于他们的task不会被执行。在需要分发消息的对象里面存储一个factory成员变量,在对象析构的时候可以做到

task

自动取消。



但是请注意:这只是在task被发往同一个线程时候才有效。如今还没有比较通用的方法用于多线程之间的任务,参考下一部分关于CancelableTaskTracker的说明。


例子如下


class MyObject {
 public:
  MyObject() : weak_factory_(this) {}

  void DoSomething() {
    const int kDelayMS = 100;
    MessageLoop::current()->PostDelayedTask(FROM_HERE,
        base::Bind(&MyObject::DoSomethingLater, weak_factory_.GetWeakPtr()),
        kDelayMS);
  }

  void DoSomethingLater() {
    ...
  }

 private:
  base::WeakPtrFactory<MyObject> weak_factory_;
};


可取消的任务


虽然base::WeakPtr很有用,但是他不是线程安全的,不能用于跨线程的任务。这种情况CancelableTaskTracker更为合适。


使用CancelableTaskTracker你可以使用任务返回的id来取消另一个线程的任务。这同样可以被应用在同一个线程。


CancelableTaskTracker相比base::TaskRunner,只是增加了任务的取消功能。


例子如下。


class UserInputHandler : public base::RefCountedThreadSafe<UserInputHandler> {
  // Runs on UI thread.
  void OnUserInput(Input input) {
    CancelPreviousTask();
    DBResult* result = new DBResult();
    task_id_ = tracker_->PostTaskAndReply(
        BrowserThread::GetMessageLoopProxyForThread(BrowserThread::DB),
        FROM_HERE,
        base::Bind(&LookupHistoryOnDBThread, this, input, result),
        base::Bind(&ShowHistoryOnUIThread, this, base::Owned(result)));
  }

  void CancelPreviousTask() {
    tracker_->TryCancel(task_id_);
  }

  ...

 private:
  CancelableTaskTracker tracker_;  // Cancels all pending tasks while destruction.
  CancelableTaskTracker::TaskId task_id_;
  ...
};



任务由于是在另一个线程执行,不能保证一定会被取消。但是可以保证不会crash吧



当我们使用TryCancel()时候,



1. 如果task和reply都没有开始执行,则都会取消



2. 如果task正在执行或者已经执行完,reply

会取消





3. 如果reply正在执行或者已经执行完,无操作



就像base::WeakPtrFactory ,CancelableTaskTracker  在析构的时候将会取消所有的task




chromium提供的线程模型确实是很高明的,没有锁的情况下提供了多种解决方案。




以下的参考很不错。第一个是我本文的主要来源,或者说翻译了部分精华内容。第二个从源码的角度分析了thread。第三个就从实现的角度讲解。




参考:




1.

http://www.chromium.org/developers/design-documents/threading




2.

http://0xffff.me/2011/12/30/%E9%97%B2%E8%AF%9Dchromium%E7%9A%84%E7%BA%BF%E7%A8%8B%E6%A8%A1%E5%9E%8B.html




3.

http://bigasp.com/archives/478



版权声明:本文为awebkit原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。