C编写的跨平台线程池 - 基于POSIX threads

在windows下使用请下载pthreads-win32

直接上源码(有比较详细的注释了),

threadpool.h

/*
 * Threadpool implementation using pthreads
 * Copyright (C) 2011 milkyjing <milkyjing@gmail.com>
 *
 * This software is provided 'as-is', without any express or implied
 * warranty.  In no event will the authors be held liable for any damages
 * arising from the use of this software.
 *
 * Permission is granted to anyone to use this software for any purpose,
 * including commercial applications, and to alter it and redistribute it
 * freely, subject to the following restrictions:
 *
 * 1. The origin of this software must not be misrepresented; you must not
 *    claim that you wrote the original software. If you use this software
 *    in a product, an acknowledgment in the product documentation would be
 *    appreciated but is not required.
 * 2. Altered source versions must be plainly marked as such, and must not be
 *    misrepresented as being the original software.
 * 3. This notice may not be removed or altered from any source distribution.
 *
 * milkyjing
 *
 */

#ifndef __THREADPOOL_H__
#define __THREADPOOL_H__
#pragma once

#include <stdlib.h>
#include <memory.h>
#include <semaphore.h>
#include <pthread.h>

#pragma comment(lib, "pthreadVC2.lib")


/* Threadpool status */
#define TP_STAT_INIT 0
#define TP_STAT_RUNNING 1
#define TP_STAT_DOWN 2

typedef void *(*tpw_func)(void *);
typedef void *tpw_args;

/**
 * ThreadpoolWork Structure
 * a work in the threadpool
 */
typedef struct _thread_pool_work {
    tpw_func execution_route;  /**< route function of the work */
    tpw_args args;             /**< parameters of the function */
    int args_deepcopy;         /**< indicates whether we should free args after we finishing the work or not */
} ThreadpoolWork, *ThreadpoolWorkPtr;

/**
 * ThreadpoolWorkQueue Structure
 * Work queue that holds works which are being dealt in the thread pool
 * queue of circular array implementation
 */
typedef struct _thread_pool_work_queue {
    ThreadpoolWorkPtr buf;   /**< pointer to the address of the queue (same as the address of the head element) */
    int size;                /**< size of the maximum works the queue can hold, should be equal to `threads' of the threadpool */
    int head_index;          /**< head of the queue */
    int rear_index;          /**< rear of the queue */
} ThreadpoolWorkQueue, *ThreadpoolWorkQueuePtr;

typedef tpw_func tp_thread_init_func;  /**< thread entrance function */

/**
 * Threadpool Structure
 * A threadpool hosts many threads, such threads are also named workers
 */
typedef struct _thread_pool {
    int threads;                           /**< number of threads in the thread pool, i.e. number of workers */
    pthread_t *ptids;                      /**< IDs of threads (workers) in the thread pool */
    pthread_mutex_t sync_mutex;            /**< mutex for synchronization */
    sem_t work_sem;                        /**< semaphore that indicates works which is under going */
    sem_t thread_sem;                      /**< semaphore that indicates available threads (idle workers) in the thread pool */
    tp_thread_init_func thread_entry;      /**< entry point for each thread, (the workers share the same door, so there is only one entry point) */
    ThreadpoolWorkQueue work_queue;        /**< work queue */
    int incompleted;                       /**< incompleted works in the thread pool */
    int tp_stat;                           /**< thread pool status, init, running, down */
    int shutdown;                          /**< shutdown = 1 indicates that the thread pool is about to shutting down */
    int completion;                        /**< set completion = 1 to check if the threadpool has completed all of the tasks assigned to it */
    pthread_cond_t completion_cond;        /**< condition variable for checking completion of the thread pool */
} Threadpool, *ThreadpoolPtr;

/* Inhibit C++ name-mangling for threadpool functions but not for system calls. */
#ifdef __cplusplus
extern "C" {
#endif    /* __cplusplus */

/**
 * Create a new threadpool
 * @param ptp pointer to ThreadpoolPtr to hold the return value, can be NULL
 * @param size number of threads the threadpool maintains
 * @return ThreadpoolPtr
 */
extern ThreadpoolPtr tp_create(ThreadpoolPtr *ptp, int size);

/**
 * Initialize the threadpool
 * @param tp pointer to the Threadpool which is going to be initialized
 * @return int 0 on success
 */
extern int tp_initialize(ThreadpoolPtr tp);

/**
 * Assign a work to the threadpool
 * @param tp pointer to the Threadpool
 * @param pwork pointer to the work that is going to be assigned to the threadpool
 * @return int 0 on success
 */
extern int tp_assign_work(ThreadpoolPtr tp, const ThreadpoolWorkPtr pwork);

/**
 * Wait for completion of all tasks assigned to the threadpool
 * @param tp pointer to the Threadpool
 */
extern void tp_wait(ThreadpoolPtr tp);

/**
 * Destroy a threadpool and free the occupied resource
 * @param tp pointer to the Threadpool
 * @param max_waiting_works indicates the maximum possible works in the waiting queue, i.e., the works that haven't entered the threadpool
 * @return int 0 on success
 */
extern int tp_destroy(ThreadpoolPtr tp, int max_waiting_works);

#ifdef __cplusplus
}
#endif    /* __cplusplus */

#endif  /* __THREADPOOL_H__ */

threadpool.c

/*
 *  Copyright (C) 2011 milkyjing <milkyjing@gmail.com>
 *
 * This software is provided 'as-is', without any express or implied
 * warranty.  In no event will the authors be held liable for any damages
 * arising from the use of this software.
 *
 * Permission is granted to anyone to use this software for any purpose,
 * including commercial applications, and to alter it and redistribute it
 * freely, subject to the following restrictions:
 *
 * 1. The origin of this software must not be misrepresented; you must not
 *    claim that you wrote the original software. If you use this software
 *    in a product, an acknowledgment in the product documentation would be
 *    appreciated but is not required.
 * 2. Altered source versions must be plainly marked as such, and must not be
 *    misrepresented as being the original software.
 * 3. This notice may not be removed or altered from any source distribution.
 *
 * milkyjing
 *
 */

#include "threadpool.h"


static int _tp_fetch_work(ThreadpoolPtr tp, ThreadpoolWorkPtr pwork) {
    sem_wait(&tp->work_sem);  /* wait for available work, if no works need to be dealt, it will be suspended */
    if (TP_STAT_DOWN == tp->tp_stat) return 0;  /* the threadpool is down, no work fetched */
    pthread_mutex_lock(&tp->sync_mutex);  /* we are going to modify a shared value, so we should lock it */
    memcpy(pwork, &tp->work_queue.buf[tp->work_queue.head_index], sizeof(ThreadpoolWork));  /* must be a copy of work in the work_queue, since work_queue may be changed right out of the lock */
    tp->work_queue.head_index = (++tp->work_queue.head_index) % tp->work_queue.size;  /* remove the fetched work */
    pthread_mutex_unlock(&tp->sync_mutex);  /* unlock the work_queue */
    return 1;
}

/* this function (may have many forks) will be executed right after the tp_initialize function, and stays idle (wait for available work) */
static void *_tp_thread_execution_route(void *args) {
    ThreadpoolPtr tp;
    ThreadpoolWork work;
    tp = (ThreadpoolPtr)args;
    while (_tp_fetch_work(tp, &work)) {  /* wait until a work comes, if no work comes, suspends */
        work.execution_route(work.args);  /* do the work */
        if (work.args_deepcopy)
            free(work.args);  /* free the memory occupied by args */
        pthread_mutex_lock(&tp->sync_mutex);
        if (!tp->shutdown)  /* if we are shutting down the threadpool, then we musn't wake up the thread to accept new works */
            sem_post(&tp->thread_sem);  /* if we are not shutting down the threadpool then, after finishing the work, we must restore the thread (worker) to idle */
        tp->incompleted--;
        if ((tp->completion || tp->shutdown) && 0 == tp->incompleted)
            pthread_cond_signal(&tp->completion_cond);
        pthread_mutex_unlock(&tp->sync_mutex);
    }
    return (void *)0;
}

ThreadpoolPtr tp_create(ThreadpoolPtr *ptp, int size) {
    ThreadpoolPtr tp;
    tp = (ThreadpoolPtr)malloc(sizeof(Threadpool));
    tp->threads = (size < 1) ? 1 : size;
    tp->ptids = (pthread_t *)malloc(tp->threads * sizeof(pthread_t));
    pthread_mutex_init(&tp->sync_mutex, NULL);
    sem_init(&tp->work_sem, 0, 0);  /* initially there is no work to be delt */
    sem_init(&tp->thread_sem, 0, tp->threads);  /* and all workers stay idle */
    tp->thread_entry = (tp_thread_init_func)_tp_thread_execution_route;  /* the same entrance (start point) for each worker */
    tp->work_queue.size = tp->threads;
    tp->work_queue.buf = (ThreadpoolWorkPtr)malloc(tp->work_queue.size * sizeof(ThreadpoolWork));
    tp->work_queue.head_index = 0;
    tp->work_queue.rear_index = 0;
    tp->incompleted = 0;
    tp->tp_stat = TP_STAT_INIT;
    tp->shutdown = 0;
    tp->completion = 0;
    pthread_cond_init(&tp->completion_cond, NULL);
    if (ptp)
        *ptp = tp;
    return tp;
}

int tp_initialize(ThreadpoolPtr tp) {
    int i, j;
    if (TP_STAT_INIT != tp->tp_stat) return -1;
    for (i = 0; i < tp->threads; i++) {
        if (pthread_create(&tp->ptids[i], NULL, tp->thread_entry, tp) != 0) {  /* now _tp_thread_execution_route function will have many forks */
            tp->tp_stat = TP_STAT_DOWN;  /* an error occurs */
            for (j = 0; j < i; j++)
                sem_post(&tp->work_sem);
            return -1;
        }
    }
    tp->tp_stat = TP_STAT_RUNNING;
    return 0;
}

int tp_assign_work(ThreadpoolPtr tp, const ThreadpoolWorkPtr pwork) {
    if (TP_STAT_RUNNING != tp->tp_stat) return -1;
    sem_wait(&tp->thread_sem);  /* wait for available thread (worker) of the thread pool */
    if (TP_STAT_DOWN == tp->tp_stat) return -1;  /* if the thread pool is down before the work being assigned to the thread pool */
    pthread_mutex_lock(&tp->sync_mutex);
    memcpy(&tp->work_queue.buf[tp->work_queue.rear_index], pwork, sizeof(ThreadpoolWork));
    tp->work_queue.rear_index = (++tp->work_queue.rear_index) % tp->work_queue.size;
    sem_post(&tp->work_sem);  /* inform some thread that a work has come */
    tp->incompleted++;
    pthread_mutex_unlock(&tp->sync_mutex);
    return 0;
}

void tp_wait(ThreadpoolPtr tp) {
    tp->completion = 1;
    pthread_mutex_lock(&tp->sync_mutex);
    if (tp->incompleted > 0)  /* if there are still some incompleted works in the thread pool */
        pthread_cond_wait(&tp->completion_cond, &tp->sync_mutex);
    tp->completion = 0;
    pthread_mutex_unlock(&tp->sync_mutex);
}

int tp_destroy(ThreadpoolPtr tp, int max_waiting_works) {
    int i;
    if (TP_STAT_DOWN == tp->tp_stat) return 0;
    else if (TP_STAT_RUNNING == tp->tp_stat) {
        tp->shutdown = 1;  /* shutting down the threadpool */
        pthread_mutex_lock(&tp->sync_mutex);
        if (tp->incompleted > 0)  /* if there are still some incompleted works in the thread pool */
            pthread_cond_wait(&tp->completion_cond, &tp->sync_mutex);
        tp->tp_stat = TP_STAT_DOWN;  /* only after we finish all the under working tasks can we set the status of the threadpool to DOWN */
        for (i = 0; i < max_waiting_works; i++)
            sem_post(&tp->thread_sem);  /* cancel the waiting works (note that now shutdown == true) */
        for (i = 0; i < tp->threads; i++)
            sem_post(&tp->work_sem);  /* wake up the idle threads */
        for (i = 0; i < tp->threads; i++)
            pthread_join(tp->ptids[i], NULL);  /* wait for completion of the works in the thread pool */
        pthread_mutex_unlock(&tp->sync_mutex);
    }
    pthread_mutex_destroy(&tp->sync_mutex);
    sem_destroy(&tp->work_sem);
    sem_destroy(&tp->thread_sem);
    pthread_cond_destroy(&tp->completion_cond);
    free(tp->ptids);
    free(tp->work_queue.buf);
    free(tp);
    return 0;
}

test_threadpool.c

/*
 *  Copyright (C) 2011 milkyjing <milkyjing@gmail.com>
 *
 * This software is provided 'as-is', without any express or implied
 * warranty.  In no event will the authors be held liable for any damages
 * arising from the use of this software.
 *
 * Permission is granted to anyone to use this software for any purpose,
 * including commercial applications, and to alter it and redistribute it
 * freely, subject to the following restrictions:
 *
 * 1. The origin of this software must not be misrepresented; you must not
 *    claim that you wrote the original software. If you use this software
 *    in a product, an acknowledgment in the product documentation would be
 *    appreciated but is not required.
 * 2. Altered source versions must be plainly marked as such, and must not be
 *    misrepresented as being the original software.
 * 3. This notice may not be removed or altered from any source distribution.
 *
 * milkyjing
 *
 */

#include "threadpool.h"
#include <stdio.h>
#include <Windows.h>

void *pp(void *args) {
    int i;
    const char *str = (const char *)args;
    for (i = 0; i < 10; i++) {
        printf("%05i %s\n", i, str);
        Sleep(1000);
    }
    return (void *)0;
}

void *aw(void *args) {
    ThreadpoolPtr tp;
    ThreadpoolWorkPtr pwork;
    tp = (ThreadpoolPtr)((unsigned long *)args)[0];
    pwork = (ThreadpoolWorkPtr)((unsigned long *)args)[1];
    tp_assign_work(tp, pwork);
    return (void *)0;
}


int main() {
    pthread_t tid;
    char sz1[1024];
    char sz2[1024];
    unsigned long aa[2];
    ThreadpoolPtr tp;
    ThreadpoolWork work;
    tp = tp_create(NULL, 3);
    if (tp_initialize(tp) != 0) {
        tp_destroy(tp, 0);
        return 0;
    }
    work.execution_route = (tpw_func)pp;
    memcpy(sz1, "Nice to meet you!", 500);
    work.args = (tpw_args)sz1;
    tp_assign_work(tp, &work);
    memcpy(sz2, "Nice to meet you, too!", 500);
    work.args = (tpw_args)sz2;
    tp_assign_work(tp, &work);
    tp_assign_work(tp, &work);
    aa[0] = (unsigned long)tp;
    aa[1] = (unsigned long)&work;
    pthread_create(&tid, NULL, aw, (void *)aa);
    //tp_assign_work(tp, &work);
    printf("Destroy: %i\n", tp_destroy(tp, 1));
    return 0;
}



  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值