多线程下task的一场处理方式

 class MyTask : public Task {
public:
virtual void Run() {
DoSomething();
}
};

ChromeThread::PostTask(ChromeThread::FILE, FROM_HERE, new MyTask);

 

有封装一个Task其实是很麻烦的事情,特别是我们经常是要调用当前对象的方法。因此Chrome里提供了NewRunnableMethod 辅助函数,可以直接把一个对象的方法封装成一个Task,这样调用PostTask就很简单了。


调用该函数,你需要提供对象指针,对象方法的指针,以及方法的参数(可以多个,类型不限)。因为是跨线程访问,所以该对象必须是线程安全的,并通过引用计数来保证对象在一个线程中访问时不被另一个线程删除了,应该要等到Task执行结束。

比如

class MyObject : public RefCountedThreadSafe<MyObject> {
public:
void DoSomething(const std;:wstring& name) {
thread_->message_loop()->PostTask(FROM_HERE
NewRunnableMethod(this, &MyObject::DoSomethingOnAnotherThread, name));
}

void DoSomethingOnAnotherThread(const std::wstring& 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_;
};


在内部,Chrome是使用Tuple来保存函数调用参数的,使用的是值拷贝,所以最好不要传递裸指针,可以带引用计数的智能指针如scoped_refptr。 比如


class SomeParamObject : public RefCountedThreadSafe<SomeParamObject> {
...
};

class MyObject : public RefCountedThreadSafe<MyObject> {
public:
void DoSomething() {
scoped_refptr<SomeParamObject> param(new SomeParamObject);
thread_->message_loop()->PostTask(FROM_HERE
NewRunnableMethod(this, &MyObject::DoSomethingOnAnotherThread, param));
}

void DoSomethingOnAnotherThread(scoped_refptr<SomeParamObject> param) {
...
}
};


甚至,你可以指定一个对象的析构在特定线程上执行,比如

class MyObject : public RefCountedThreadSafe<MyObject, ChromeThread::DeleteOnIOThread> {


因为Task是异步执行的,就有可能在执行Task的时候,它要访问的对象已经被删除了,这是多线程程序里经常见的一种崩溃模式。所以,最好能在对象被删除前,首先取消所有未执行的相关Task。Chrome的解决办法是,使用工厂对象来创建Task,并把工厂对象作为被调用对象的成员变量。这样,在对象析构时,工厂对象首先被析构并自动取消所有由其创建的Task。使用这种方法,不需要被调用对象使用引用计数来控制生命周期,相对简单。

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

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

void DoSomethingLater() {
...
}

private:
ScopedRunnableMethodFactory<MyObject> factory_;
};


前面使用工厂对象可以避免对象删除后执行Task引发崩溃,另外还有一种类似的情形就是发出一个请求后又想取消。同样的,Chrome也是使用一个额外的对象来作为跟踪之用,当该对象被删除时自动取消未完成的请求。

class MyClass {
void MakeRequest() {
frontend_service->StartRequest(some_input1, some_input2, this,
NewCallback(this, &MyClass:RequestComplete));
}

void RequestComplete(int status) {
...
}

private:
CancelableRequestConsumer consumer_;
};

处理请求的对象必须是从CancelableRequestProvider派生的,该对象有方法可以取消还没有被执行的请求,并且保证调用者也能得到执行相关清理工作的机会。 如果不需要等待请求的结果,则没有必要使用这个方法,而直接使用Task/RunnableMethod即可。

下面的例子显示的是,一个前台模块接到请求时,直接就把请求转给了后台模块。

class FrontendService : public CancelableRequestProvider {
typedef Callback1::Type RequestCallbackType;

Handle StartRequest(int some_input1, int some_input2,
CallbackConsumer* consumer,
RequestCallbackType* callback) {
scoped_refptr > request(
new CancelableRequest(callback));
AddRequest(request, consumer);

// Send the parameters and the request to the backend thread.
backend_thread_->PostTask(FROM_HERE,
NewRunnableMethod(backend_, & BackendService::DoRequest, request,
some_input1, some_input2), 0);

// The handle will have been set by AddRequest.
return request->handle();
}
};

后台模块是这样返回处理结果的

class BackendService : public RefCountedThreadSafe<BackendService> {
void DoRequest(
scoped_refptr< CancelableRequest<Frontend::RequestCallbackType> >
request,
int some_input1, int some_input2) {
if (request->canceled())
return;

... do your processing ...

// Depending on your typedefs, one of these two forms will be more
// convenient:
request->ForwardResult(Tuple1<int>(return_value));

// -- or -- (inferior in this case)
request->ForwardResult(FrontendService::RequestCallbackType::TupleType(
return_value));
}
};

©️2020 CSDN 皮肤主题: 大白 设计师: CSDN官方博客 返回首页
实付0元
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值