Coroutine to add synchronization mutual exclusion mechanism

Previous article introduces the concept of mutual exclusion synchronization within Linux, kernel mode and user mode Linux provides synchronous / exclusive interface. Under article describes how to add synchronization here coroutine, mutual exclusion mechanism.


Simply under the coroutine coroutine:

Reference article

OS textbook definition of processes, threads: the process is the smallest unit of resource allocation, scheduling thread is the smallest unit. With the rapid development of the Internet, the Internet Server back-end services usually face high request, high concurrency challenges, some business Server typically face high network IO requests. This is the C10K problem. Now C10K solution to the problem has been very mature, mainly non-blocking IO + IO multiplexing (epoll, select, etc.) + network event-driven, with an additional multi-process / multi-threaded. Such non-blocking IO + IO multiplexing + network solution for event-driven, we usually called asynchronous mode, want to cope with the synchronous mode.

Here is a simple example:

For service srvA, for each front end request logic is as follows: Req receive front end, SrvB pull data access, then access SrvC pull data, packet back to the front end Rsp

For synchronous mode solution:

Must have a thread or process for each front-end Req be processed until the return packet network access front-end logic process usually blocking mode; whether it is a process used to produce the thread pool / pool or process or thread to each request when treatment ends when the current request volume, there is a lot of system processes / threads, thread scheduling, kernel resources are occupied by more serious problem.

For asynchronous mode solution:

+ Non-blocking IO using the IO multiplexing, can lead to blockage of the operation into an asynchronous operation, the main thread is responsible for initiating the asynchronous operation, and process the results of this asynchronous operation. Since all operations are blocked into asynchronous operation, in theory, the main thread of most of the time in dealing with the actual computing tasks, less time scheduling multiple threads, one thread will be able to simultaneously handle a large number of client requests, so this the performance of two models usually better. But this model, various callback, request a front-end processing logic is dispersed in various places in the code, the development of high maintenance costs.

The recent rise of the coroutine solution:

Coroutine to be used to make the original way to write asynchronous callback + non-human code, you can use synchronized manner seemingly written, the performance is close to the asynchronous mode. Cheng is a lightweight user-level threads. Coroutine has its own stack and register context. When coroutine scheduled handover, the context save registers and stack to another location, when cut back, context restore a previously saved registers and stack. Thus: coroutine can be left as the last call (i.e., a particular combination of all of the local state), during each reentrant, the equivalent of entering a call state, another way: once on entering the leaving at the logic flow position. In concurrent programming coroutine thread Similarly, each represents a coroutine execution unit has its own local data, shared global data and other resources with other coroutine. Basically the current mainstream language chosen as concurrent multi-threading facilities, related to the concept of threads is preemptive multitasking (Preemptive multitasking), and is associated with the coroutine cooperative multitasking.

About thread, the state machine model:

A Computer is a state machine. Threads are for people who can not program state machines. In fact, probably meaning the computer has always been a state machine, each thread, the process will look to have their own state as an entity by the scheduler to schedule various hardware events, time time to drive each entity state change. Specific online search under.

Several support coroutine language library

  1. go-lang multiple threads, each of which a plurality of co-routines, may also be scheduled coroutine (transferred from one thread to another thread)
  2. erlang
  3. take
  4. c / c ++: libgo supports multithreading
  5. c / c ++: micro-channel libco support multithreading
  6. boost library context, coroutine
  7. I write uthread, it is a simple example, coroutine library in single threaded process.
  8. These languages, some library implements synchronization, mutual exclusion mechanism between the coroutine. For example golang the channel, libgo also provides a spin lock.
  9. PS: We can look off above large columns  to co-drive with synchronization mutual exclusion mechanism to implement coroutines, which co-switching process and the Linux kernel process / thread scheduling switch similar to: switch_to. In fact, I think of Linux processes, threads, to achieve the above coroutine implementation is the use of a state machine model.

Mutex to synchronize the interface coroutine

The author's logic layer framework with the corporate sector in the integration process of the Association, the logical layer usually provide RPC services, each front-end request packet must return a back pack, access the back-end business logic generally service or other logical DB services. Each logical layer frame front end generates a request for a coroutine, when developers to access back-end services, call coroutine network interface API library complete coroutine scheduler (cut out while waiting for an event, when the wake-up event arrives), so to achieve synchronization code asynchronous model results.

Framework coroutine library is a single-process, single-threaded (how to take advantage of multi-core machine, you can play multiple processes), because rarely require collaboration among multiple coroutines, and coroutine switching timing is controlled developer knowable, so this coroutine library does not provide mutual exclusion between coroutine, synchronization mechanisms. (Uthread examples of their own implementation, but also only supports single-process single-threaded, there is no synchronization mutex interface.)

Recent work for some reason, so try to department coroutine library with synchronization, mutual exclusion Interface: semaphore; mutex; condition variable; because there is no thread is single-threaded competition so here is relatively simple to implement

signal:

/**
 *@brief 微线程的信号量 , 用于微线程间的同步
 */
class MtSem
{
    public:
        uint32_t  sem_seq( )
        {
            return _dwSemSeq;
        };
        friend  class MtFrame;
     private: 
        uint32_t  _dwSemSeq;     //信号量资源的唯一标识, Init时调度器分配
        int32_t  _dwSemInitValue;   //信号量的初始值,参考sem_init的第三个参数  
        int32_t  _dwSemCurValue;
        std::list<MicroThread *> _waitList;   //当资源消耗完时的 等待队列
};

int  MtFrame::SemInit(uint32_t  value )
{
    uint32_t seq = _nextSemSeq+1;
    _nextSemSeq++;
    if(seq == 0 )
    {
        seq++;
        _nextSemSeq++;
    }
    if(  _semMng.find(seq) != _semMng.end ()) 
    {
        MTLOG_ERROR("Init Sem Error  %d", errno);
        return 0; //资源消耗完
    }
    MtSem * sem = new MtSem;
    sem->_dwSemSeq = seq;
    sem->_dwSemInitValue = sem->_dwSemCurValue = value;
    _semMng[seq] = sem;
    return seq;
}

int MtFrame::SemWait(uint32_t semSeq  )
{ //等待
    if( _semMng.find(semSeq) == _semMng.end() )
    {
        MTLOG_ERROR("semSeq  %d not in semMng",semSeq);
        return -1;
    }
    MtSem * sem = _semMng[semSeq];
    if(sem == NULL )
    {
        MTLOG_ERROR("MtSem:%d is NULL ", semSeq);
        return -1;
    }

    if( sem->_dwSemCurValue <= 0 ) 
    {//挂住线程 
       MtFrame* mtframe = MtFrame::Instance();
       MicroThread* thread =mtframe->GetActiveThread();
        sem->_waitList.push_back(thread); 
        thread->sleep(0x7fffffff); //睡眠最大时间, 切走线程
        sem->_dwSemCurValue--; //且回来时肯定是_dwSemCurValue 已经>0了
    }
    else // _dwSemCurValue > 0
    {
        sem->_dwSemCurValue --;
    }
    return 0;
}
int MtFrame::SemPost(uint32_t semSeq )
{
    if( _semMng.find(semSeq) == _semMng.end() )
    {
        MTLOG_ERROR("semSeq  %d not in semMng",semSeq);
        return -1;
    }

    MtSem * sem = _semMng[semSeq];
    if(sem == NULL )
    {
        MTLOG_ERROR("MtSem:%d is NULL ", semSeq);
        return -1;
    }

    if(sem->_dwSemCurValue > 0 )
    { //没有线程挂住
       sem->_dwSemCurValue++;
    }

    else
    { //_dwSemCurValue <=0. 应该只能==0,不会小于0
      //有线程挂住,唤醒,唤醒几个呢?每次唤醒一个即可。因为也只增加了1个
       
        sem->_dwSemCurValue++; 
        if( sem->_waitList.size()> 0 )
        { //找到队列头部的线程,头部的先压入的,所以先唤醒
            MicroThread *thread = sem->_waitList.front();
            sem->_waitList.pop_front(); //从等待队列头删除
            //从调度器的睡眠列迁移到可运行队列
            RemoveSleep(thread);
            InsertRunable(thread);
        }
        else
        { //never run here
        }
    }
    return 0;
}

Mutex:

Condition variables:

Guess you like

Origin www.cnblogs.com/sanxiandoupi/p/11712797.html
Recommended