Skip to the content.

协程说明

tarscpp 3.x全面启用对协程的支持!

服务框架全面融合协程, 使得使用者可以方便的使用协程, 本文主要介绍如何使用协程.

关于协程, 你需要重点的了解以下几点概念:

框架和协程相关的类

协程启动的方式

通常在业务服务中, 不需要自己去启动协程, 只需要根据服务模型配置服务模型即可, 服务框架会根据配置, 自动将服务的线程变成协程, 即处理业务代码在协程中执行, 此时发起rpc调用即使是同步方式, 会变成协程模式, 从而变成异步模式, 具体后续文档中会介绍.

当然如果你想自己控制启动协程, 可以参考以下方式.

线程转协程

如果你处于任何一个线程中, 你希望在当前线程能调度协程, 你可以参考以下示例代码

auto scheduler = TC_CoroutineScheduler::create();
scheduler->setPoolStackSize(10, 128*1024);
scheduler->go([&]()
{
    scheduler->setNoCoroutineCallback([=](TC_CoroutineScheduler* s)
    {
        s->terminate();
    });

    cout << "incoroutine" << endl;
});

scheduler->run();

说明:

TC_Thread

自己创建调度器, 控制调度毕竟还是麻烦, 因此提供了扩展了TC_Thread是的更加方便.

TC_Thread在9.x以前版本中, 代表的线程, 当你需要实现线程的时候, 可以继承TC_Thread, 然后实现run方法, 调用start即可启动一个线程.

在9.x版本中, 你可以同样的方式继承TC_Thread, 但是调用startCoroutine方法启动协程, 即此时run方法处于协程中, 比如以下示例代码:


class CoThread : public TC_Thread
{
public:
	CoThread() {}

	virtual void run()
	{
		cout << "in coroutine" << endl;	
	}
};

//创建了一个线程
CoThread* a = new CoThread();

//以协程方式启动的
a->startCoroutine(10, 128*1024, true);

//等待线程结束
a->join();

delete a;

说明:

进一步, 在协程中启动更多的协程:


class CoThread1 : public TC_Thread
{
public:
	CoThread1() {}

	virtual void run()
	{
        //使用调度器以协程方式启动其他协程
		TC_CoroutineScheduler::scheduler()->go(std::bind(&CoThread1::doCo, this));
	}

	void doCo()
	{
		CoThreadDo = true;
	}
};

CoThreadDo = false;

CoThread1* a = new CoThread1();

a->startCoroutine(10, 128*1024, true);

a->getThreadControl().join();

ASSERT_TRUE(CoThreadDo);

delete a;

说明:

TC_Coroutine

这个类可以一次创建多个协程出来, 拥有TC_Thread之后, 这个类其实用得相对较少, 示例代码如下:


class MyCoroutine : public TC_Coroutine
{
protected:
	void handle()
	{
		++_count;

		this->go(std::bind(&MyCoroutine::co_test, this));
	}

	void co_test()
	{
		++_count;
	}

public:
	static atomic<int> _count;
};

atomic<int> MyCoroutine::_count{0};

MyCoroutine::_count = 0;

MyCoroutine co;

co.setCoroInfo(10, 200, 128*1024);

co.start();

co.join();

说明:

服务模型和协程

服务模型扩展成四种, 同时服务模型下层到tc_epoll_server中, 即最底层的服务也能设置协程模型, 示例可以参考源码中: unittest/test_tc_epoller_server.cpp, 四个服务模型中和协程相关的重点是:

说明:

对于底层的epollserver, 可以通过api控制服务的模型和协程参数:

//设置服务模型
TC_EpollServer::setOpenCoroutine

//设置协程池梳理和栈大小
TC_EpollServer::setCoroutineStack

对于rpc服务而言, 可以通过参数控制模型, 修改模板即可

<taf>
    <application>
        <server>
            coroutinememsize=1073741824
            coroutinestack=131072
            opencoroutine=0
        </server>
    </application>
</taf>

注意:

通信器是rpc调用的客户端资源集合体, 理解它的模型是非常关键的.

普通的通信器模型

9.x以前的版本我们称之为普通的通信器模型, 具体说明如下:

协程模式下的通信器

协程的版本诞生可以减少线程的切换调度, 降低rpc的延时, 协程模式的网络通信器确实很复杂很多, 我们来看一下协程模式下通信器的设计:

这种模式下具体的rpc逻辑如下:

如何开启协程模式的通信器

并不是只要业务线程启用了协程, 就默认开启了协程模式的通信器, 还需要做以下处理:

ServantProxyThreadData::getData()->_sched = TC_CoroutineScheduler::scheduler();

只有这样设置以后, 通信器才会感知到业务处于协程模式, 且启用协程网络通信器.

**注意老版本协程模式相当于 opencoroutine=1 **

NET_THREAD_QUEUE_HANDLES_CO & NET_THREAD_MERGE_HANDLES_CO 两种模式下, 服务端的业务处理线程, 默认就已经设置了, 这样在服务器业务线程中发起rpc时, 本质上是协程模式, 网络收发都在业务线程中处理的!

协程模式下有哪些影响