• c++之初级的消息队列及线程池模型


    1.最近项目不是很忙,结合之前看的一些开源代码(skynet及其他github代码)及项目代码,抽空写了一个简单的任务队列当做练习。

    2.介绍:

      1)全局队列中锁的使用:多线程下,全局队列需要加锁,本例中封装了MutexGuard。操作全局队列之前,先在栈上创建一个临时锁对象,调用构造函数时加锁,对象销毁时调用析构函数从而解锁,减少了我们手动加锁,解锁的过程。

      2)信号的使用:本例可以说是为了使用信号而使用信号,仅仅是为了熟悉信号机一些特性。 当程序以后台模式 跑起来以后,输入kill -USR1 %1 向程序发送SIGUSR1信号,从而使生产者生产一定数量的job,供消费者使用;消费者线程,在处理完全局队列以后sleep,等待生产者产生新任务; 输入 kill -USR2 %1, 改变变量状态,向信号监听线程发送结束通知,结束线程。

      3)简单的线程池模型。

      4)简单的线程间通信和同步方式示例。

      5)简单的类模板的使用。

    3.编译: 文件不多,偷懒没有写makefile文件,可自行加上。编译指令 : g++ -g -Wall -o test main.cpp mutex.cpp List.h mutex.h -lpthread

    4:执行流程:

      1)编译成功后,输入 ./mytest &。 以后台模式运行程序

      2)此时所有consumer线程阻塞,等待生产者生产job; 一个producer线程阻塞在select处,等待读管道内的消息;一个signal_handler线程调用 pthread_sigwait( ... ) 等待 SIGUSR1 和 SIGUSR2 信号的到来。

    可通过在控制台输入: kill -USR1 %1(ps: kill 指令用来产生信号 当以后台模式运行该进程时, %1用来获得该进程 id,因此该命令表示向 该进程发送 SIGUSR1 信号)进程发送SIGUSR1信号,被signal_handler捕捉到以后,生产job,唤醒consumer线程处理job,此流程可重复执行;当在控制台输入 kill -USR2 %1 时, 改变quit变量值,从而使得各个线程退出,进程结束。还有一个 spoliling 轮询线程,在全局队列不为空的情况下,及时唤醒consumer线程处理任务。可通过调整wakeup中的参数,调整唤醒consumer的频率。

    5.参考:

      1)UNIX环境高级编程。

      2)https://github.com/idispatch/signaltest  

      3)https:github.com/cloudwu/skynet/skynet-src/skynet_start.c

    水平有限,仅供参考,希望能对读者有所帮助。以上描述及以下源码有任何漏洞与不足,欢迎及时指正与交流。

     6:源码:

    main.cpp:

    #include <iostream>
    #include <stdio.h>
    #include <stdlib.h>
    #include <pthread.h>
    #include <unistd.h>
    #include <sys/select.h>
    #include <sys/types.h>
    #include <string.h>
    #include <errno.h>
    #include <signal.h>
    
    #include "List.h"
    #include "mutex.h"
    
    #define THREAD_NUM 4
    #define JOB_NUM 100
    
    #define handle_error_en(en, msg) 
            do{ errno = en; perror(msg); exit(EXIT_FAILURE); } while(0)
    
    using std::cout;
    using std::endl;
    using std::string;
    using std::cin;
    
    struct monitor 
    {
        int count;
        pthread_cond_t cond;
        pthread_mutex_t mutex;
        int sleep;
        int quit;
        int pfds[2];
    };
    
    struct sig
    {
        sigset_t set;
        struct monitor *m;
    };
    
    typedef void (*thread_func)(void *arg, int value);    //job call back
    struct job
    {
        void *arg;
        thread_func cb;
    };        
             
    List<job *> g_list;      
    const int allowed_signals[] = {SIGUSR1, SIGUSR2, SIGQUIT};   
    
    static void
    print_v(void *value, int pid)
    {    
        printf("pid: %d, value: %d
    ", pid, *(int*)value);
    } 
    
    static void
    free_job(struct job *j)
    {
        if(j == NULL)
        {
            return;
        }
    
        free(j->arg);
        j->arg = NULL;
        free(j);
        j = NULL;
    }
    
    static int 
    dispatch(int pid)
    {              
        struct job *j = g_list.Pop();
        if (j != NULL)
        {
            j->cb(j->arg, pid);
            free_job(j);
            return 0;
        }
    
        return -1;
    }        
        
    static void * 
    consumer(void *arg)
    {    
        struct monitor *m = (struct monitor *)arg;
        int r = 0;
    
        usleep(50000);
        int pid = pthread_self();
        while(!m->quit)    
        {                
            r = dispatch(pid);
            if (r < 0)
            {        
                if(pthread_mutex_lock(&m->mutex) == 0)
                {    
                    ++m->sleep;
                    cout << "thread : " << pid << " sleep" << endl;
                    if(!m->quit)
                    {
                        pthread_cond_wait(&m->cond, &m->mutex);
                    }
    
                    -- m->sleep;
                    cout << "thread : " << pid << " wakeup" << endl;
                    if(pthread_mutex_unlock(&m->mutex))
                    {
                        fprintf(stderr, "unlock mutex error");
                        exit(1);
                    }
                }    
            }        
        }        
        cout << "thread consumer quit " << endl;
        return NULL;    
    }                    
    
    static void
    free_monitor(struct monitor *m)
    {
        if(m == NULL)
        {
            return;
        }
        cout << "free monitor called" << endl;
        close(m->pfds[0]);
        close(m->pfds[1]);
    
        free(m);
        cout << "free monitor over" << endl;
    }
        
    static void
    wakeup(struct monitor *m, int busy) 
    {
        if (m->sleep >= m->count - busy) 
        {
            // signal sleep worker, "spurious wakeup" is harmless
            pthread_cond_signal(&m->cond);
        }
    }    
        
            
    static struct job*
    create_job()
    {    
        struct job * j = (struct job *)calloc(1, sizeof(*j));
        if (j == NULL)
        {
            fprintf(stderr, "create_job failed");
            return NULL;
        }
        
        int v = rand();
        j->arg = malloc(sizeof (int));
        if (j->arg == NULL)
        {
            fprintf(stderr, "get arg failed");
            return NULL;
        }
        memcpy(j->arg, &v, sizeof (int) );
        j->cb = print_v;
            
        return j;
    }    
        
    static void * 
    producer(void *arg)
    {    
        
        struct monitor *m = (struct monitor *)arg;    
        cout << "producer called" << endl;
        int pid = pthread_self();
        int state;
        while(!m->quit)
        {
            fd_set fds;
            FD_ZERO(&fds);
            FD_SET(m->pfds[0], &fds);
    
            state = select(m->pfds[0] + 1, &fds, NULL, NULL, NULL);
            if(state < 0)
            {
                if(errno == EINTR)
                {
                    cout << "errno == EINTR" << endl;
                    continue;
                }
                break;
            }
            else if (state == 0)
            {
    
            }
            else
            {
                char msg[200];
                memset(msg, 0, sizeof(msg));
                read(m->pfds[0], msg, sizeof(msg)); //only to clear up pipe.
                msg[strlen(msg)] = '';
                fprintf(stdout, "msgis: %s
    ", msg);
                fflush(stdout);
    
                if (FD_ISSET(m->pfds[0], &fds))
                {
                    if(strncmp(msg, "quit", strlen("quit")) == 0)
                    {
                        break;
                    }
    
                    int i;
                    for (i = 0; i < JOB_NUM; i++)
                    {
                        struct job *j = create_job();
                        if (j == NULL)
                        {
                            fprintf(stderr, "prodecer failed");
                            exit(1);
                        }
                        g_list.Push(j);
                    }    
                    cout << "Thread " << "[" << pid << "]" << ": create " << JOB_NUM << " jobs" << endl;
                    wakeup(m, 2);
                }
            }
        }
    
        cout << "thread producer quit" << endl;
        return NULL;
    }        
    
    static int
    check_g_list()
    {        
        int len = g_list.get_job_num();
        if(len == 0 )
        {    
            return -1;
        }    
    
        return 1;
    }        
            
    static void *    
    spoiling(void *arg)
    {        
        struct monitor *m = (struct monitor *)arg;
        cout << "spoiling called" << endl;
        while(!m->quit)
        {
            int n = check_g_list();
            if(n == 0)
            {
                break;
            }
            if(n < 0)
            {
                continue;
            }
            wakeup(m, 1);
        }        
    
        cout << "thread spoiling quit" << endl;
        return NULL;
    }        
            
    static void
    thread_create(pthread_t *pid, void *arg , void * (*pthread_func) (void *))
    {
        if(pthread_create(pid, NULL, pthread_func, arg) != 0)
        {
            fprintf(stderr, "create_thread failed");
            exit(1);
        }
    }    
    
    static void*
    signal_handler(void *arg)
    {    
        struct monitor *m = (struct monitor *)arg;
        int isig, state;
    
        sigset_t set;
        sigemptyset(&set);
        sigaddset(&set, SIGUSR1);
        sigaddset(&set, SIGUSR2);
        sigaddset(&set, SIGTERM);
    
        cout << "signal_handler called" << endl;
        for(;;)
        {
            state = sigwait(&set, &isig);
            cout << "sigwait : " << isig << endl;
            if(state != 0)
            {
                fprintf(stderr, "wrong state %d
    ", state);
                continue;
            }
            if(isig == SIGUSR1)
            {
                cout << "SIGUSR1 " << endl;
                char msg[200];
                memset(msg, 0, sizeof(msg));
                snprintf(msg, sizeof(msg), "signal_handler: received signal=%d(thread=%d)
    ", isig, (int)pthread_self());
                write(m->pfds[1], msg, strlen(msg));
            }
            else if(isig == SIGUSR2)
            {
                cout << "SIGUSR2 " << endl;
                pthread_mutex_lock(&m->mutex);
                m->quit = 1;
                write(m->pfds[1], "quit", strlen("quit"));
                pthread_cond_broadcast(&m->cond);
                pthread_mutex_unlock(&m->mutex);
                
                //when quit, send "quit" to producer or it will block on select
                
                break;
            }
            else
            {
                cout << "SIG OTHER quit" << endl;
                break;
            }
        }
        cout << "signal_handler quit" << endl;
        return NULL;
    }        
            
    static void 
    start_thread()
    {    
        pthread_t pids[THREAD_NUM + 3];    
        struct monitor *m = (struct monitor *)malloc(sizeof(*m));//(struct monitor *)malloc(sizeof(*m));
        if (m == NULL)
        {    
            fprintf(stderr, "create monitor failed");
            exit(1);
        }    
        if(pipe(m->pfds))
        {
            fprintf(stderr, "%s: pipe failed
    ", __FUNCTION__);
            exit(1);
        }
    
        m->count = THREAD_NUM;
        m->sleep = 0;
        m->quit = 0;
        if(pthread_mutex_init(&(m->mutex), NULL) != 0 || pthread_cond_init(&(m->cond), NULL) != 0)
        {        
            fprintf(stderr, "mutex or cond init failed");
            exit(1);
        }        
    
        int rc;
        sigset_t set;
    
        sigemptyset(&set);
        sigaddset(&set, SIGUSR1);
        sigaddset(&set, SIGUSR2);
        sigaddset(&set, SIGQUIT);
        rc = pthread_sigmask(SIG_BLOCK, &set, NULL);
        if(rc != 0)
        {
            fprintf(stderr, "%s pthread_sigmask failed
    ", __FUNCTION__);
            exit(1);
        }
    
        thread_create(&pids[0], m, signal_handler);    
        thread_create(&pids[1], m, spoiling); //spoiling thread , check if the g_list is empty 
        thread_create(&pids[2], m, producer); //producer thread 
    
        int i; 
        for (i = 3; i < THREAD_NUM + 3; i++) 
        { 
            thread_create(&pids[i], m, consumer); //consumer thread 
        } 
        
        for (i = 0; i < THREAD_NUM + 3; i++)
        {
            pthread_join(pids[i], NULL);
        }
    
        free_monitor(m);
    }    
            
    int 
    main(int argc, char *argv[])
    {    
        cout << "-----------------start---------------------" << endl;
        start_thread();
        cout << "------------------end----------------------" << endl;
    }    
    View Code

    mutex.h

    #ifndef __MUTEX__H__
    #define __MUTEX__H__
    
    #include <list>
    
    #include <pthread.h>
    
    class MyMutex
    {
        public:
            MyMutex(pthread_mutex_t& m);
            ~MyMutex();
    
            void Lock();
            void UnLock();
    
        private:
            pthread_mutex_t& m_m;
    
    };
    
    class MyMutexGuard
    {
        public:
            MyMutexGuard(pthread_mutex_t& m);
            ~MyMutexGuard();
    
        private:
            MyMutex mm;
    
    };
    
    
    #endif
    View Code

    mutex.cpp

    #include "mutex.h"
    
    MyMutex::MyMutex(pthread_mutex_t& m) : m_m(m)
    {
    }
    
    
    MyMutex::~MyMutex()
    {
    }
    
    void MyMutex::Lock()
    {
        pthread_mutex_lock(&m_m);
    }
    
    
    void MyMutex::UnLock()
    {
        pthread_mutex_unlock(&m_m);
    }
    
    MyMutexGuard::MyMutexGuard(pthread_mutex_t& m):mm(m)
    {
        mm.Lock();
    }
    
    MyMutexGuard::~MyMutexGuard()
    {
        mm.UnLock();
    }
    View Code

    List.h

    #ifndef __LIST_HEAD__
    #define __LIST_HEAD__
    
    #include "mutex.h"
    
    #include <list>
    using std::list;
    #ifndef _WIN32
    #include <pthread.h>
    #endif
    
    template<typename T>
    class List
    {
        public:
            List();
            List(const list<T> &l);
            virtual ~List();
    
            T Pop();
            void Push(const T t);
            bool Empty();
            int get_job_num();
        private:
            void init();
            void destroy();
    
        private:
            bool m_init;
            list<T> my_list;
            pthread_mutex_t mm;
    
    };
    
    #include "List.cpp"
    
    #endif
    View Code

    List.cpp

    #include "List.h"
    #include "mutex.h"
    
    template<typename T>
    List<T>::List() 
        :m_init(false)
    {
        
    }
    
    template<typename T>
    List<T>::List(const list<T> &l)
        :m_init(false)
    {
    
    }
    
    template<typename T>
    List<T>::~List()
    {
        destroy();
    }
    
    template<typename T>
    void List<T>::Push(const T t)
    {
        MyMutexGuard g(mm);
        my_list.push_back(t);
    }
    
    template<typename T>
    T List<T>::Pop()
    {
        MyMutexGuard g(mm);
    
        if(my_list.empty())
        {
            return NULL;
        }
        else
        {
            T tt = my_list.front();
            my_list.pop_front();
    
            return tt;
        }
    }
    
    template<typename T>
    bool List<T>::Empty()
    {
        MyMutexGuard g(mm);
        return my_list.empty();
    }
    
    template<typename T>
    void List<T>::init()
    {
        if(!m_init)
        {
            m_init = (pthread_mutex_init(&mm, NULL) == 0);
        }
    
        return m_init;
    }
    
    template<typename T>
    void List<T>::destroy()
    {
        pthread_mutex_destroy(&mm);
    }
    
    template<typename T>
    int List<T>::get_job_num()
    {
        MyMutexGuard g(mm);
        return my_list.size();
    }
    View Code
  • 相关阅读:
    162. Find Peak Element
    475. Heaters
    字符串统计
    数据的交换输出
    偶数求和
    青年歌手大奖赛_评委会打分
    蟠桃记
    素数判定
    多项式求和
    出现Presentation Error的解决方法
  • 原文地址:https://www.cnblogs.com/newbeeyu/p/6528634.html
Copyright © 2020-2023  润新知