C语言-单例模式实现线程池

Posted Michael_Good

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了C语言-单例模式实现线程池相关的知识,希望对你有一定的参考价值。

C语言单例模式实现线程池。
该代码中,使用了单例模式来创建线程池对象,保证了整个程序中只有一个线程池对象。
线程池中包含了任务队列、工作线程数组、互斥锁、条件变量等成员,通过这些成员来实现任务的提交和执行。
在主函数中,提交了10个任务,每个任务都是一个简单的打印数字的函数,最后等待所有任务执行完毕后销毁线程池。

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>

#define THREAD_POOL_SIZE 5

// 任务结构体
typedef struct 
    void (*task)(void*);
    void* arg;
 Task;

// 线程池结构体
typedef struct 
    Task* tasks; // 任务队列
    int size; // 任务队列大小
    int head; // 任务队列头指针
    int tail; // 任务队列尾指针
    int count; // 任务队列中任务数量
    pthread_mutex_t lock; // 互斥锁
    pthread_cond_t not_empty; // 非空条件变量
    pthread_cond_t not_full; // 非满条件变量
    int shutdown; // 线程池是否关闭
    pthread_t* threads; // 工作线程数组
    int thread_count; // 工作线程数量
 ThreadPool;

// 线程池单例结构体
typedef struct 
    ThreadPool* pool; // 线程池指针
 ThreadPoolSingleton;

static ThreadPoolSingleton* instance = NULL; // 线程池单例对象指针

// 工作线程函数
void* worker(void* arg) 
    ThreadPool* pool = (ThreadPool*)arg;
    while (1) 
        pthread_mutex_lock(&pool->lock);
        while (pool->count == 0 && !pool->shutdown) 
            pthread_cond_wait(&pool->not_empty, &pool->lock);
        
        if (pool->count == 0 && pool->shutdown) 
            pthread_mutex_unlock(&pool->lock);
            pthread_exit(NULL);
        
        Task task = pool->tasks[pool->head];
        pool->head = (pool->head + 1) % pool->size;
        pool->count--;
        pthread_cond_signal(&pool->not_full);
        pthread_mutex_unlock(&pool->lock);
        task.task(task.arg);
    
    return NULL;


// 创建线程池函数
ThreadPool* create_thread_pool(int thread_count, int queue_size) 
    ThreadPool* pool = (ThreadPool*)malloc(sizeof(ThreadPool));
    pool->tasks = (Task*)malloc(sizeof(Task) * queue_size);
    pool->size = queue_size;
    pool->head = 0;
    pool->tail = 0;
    pool->count = 0;
    pthread_mutex_init(&pool->lock, NULL);
    pthread_cond_init(&pool->not_empty, NULL);
    pthread_cond_init(&pool->not_full, NULL);
    pool->shutdown = 0;
    pool->threads = (pthread_t*)malloc(sizeof(pthread_t) * thread_count);
    pool->thread_count = thread_count;
    for (int i = 0; i < thread_count; i++) 
        pthread_create(&pool->threads[i], NULL, worker, pool);
    
    return pool;


// 销毁线程池函数
void destroy_thread_pool(ThreadPool* pool) 
    pthread_mutex_lock(&pool->lock);
    pool->shutdown = 1;
    pthread_mutex_unlock(&pool->lock);
    pthread_cond_broadcast(&pool->not_empty);
    for (int i = 0; i < pool->thread_count; i++) 
        pthread_join(pool->threads[i], NULL);
    
    free(pool->threads);
    free(pool->tasks);
    pthread_mutex_destroy(&pool->lock);
    pthread_cond_destroy(&pool->not_empty);
    pthread_cond_destroy(&pool->not_full);
    free(pool);


// 提交任务函数
void submit_task(ThreadPool* pool, void (*task)(void*), void* arg) 
    pthread_mutex_lock(&pool->lock);
    while (pool->count == pool->size && !pool->shutdown) 
        pthread_cond_wait(&pool->not_full, &pool->lock);
    
    if (pool->shutdown) 
        pthread_mutex_unlock(&pool->lock);
        return;
    
    pool->tasks[pool->tail].task = task;
    pool->tasks[pool->tail].arg = arg;
    pool->tail = (pool->tail + 1) % pool->size;
    pool->count++;
    pthread_cond_signal(&pool->not_empty);
    pthread_mutex_unlock(&pool->lock);


// 任务函数
void task_func(void* arg) 
    int* num = (int*)arg;
    printf("task %d is running\\n", *num);
    free(num);


// 任务包装函数
void* task_wrapper(void* arg) 
    TaskWrapper* wrapper = (TaskWrapper*)arg;
    submit_task(wrapper->pool, wrapper->task, wrapper->arg);
    free(wrapper);
    return NULL;


init_instance() 
	instance = (ThreadPoolSingleton*)malloc(sizeof(ThreadPoolSingleton));
	instance->pool = create_thread_pool(THREAD_POOL_SIZE, THREAD_POOL_SIZE);

// 获取线程池单例对象函数
ThreadPool* get_thread_pool_instance() 
    return instance->pool;


int main() 
	init_instance();	/* 程序一开始,就必须执行。不然,与懒汉式无较大差异 */
    ThreadPool* pool = get_thread_pool_instance(); // 获取线程池单例对象
    for (int i = 0; i < 10; i++) 
        int* num = (int*)malloc(sizeof(int));
        *num = i;
        TaskWrapper* wrapper = (TaskWrapper*)malloc(sizeof(TaskWrapper));
        wrapper->pool = pool
		wrapper->task = task_func;
		wrapper->arg = num;
		pthread_t tid;
		pthread_create(&tid, NULL, task_wrapper, wrapper); // 提交任务
	
	sleep(1); // 等待所有任务执行完毕
	destroy_thread_pool(pool); // 销毁线程池
	return 0;


/*
该示例代码中,使用了单例模式来创建线程池对象,保证了整个程序中只有一个线程池对象。
线程池中包含了任务队列、工作线程数组、互斥锁、条件变量等成员,通过这些成员来实现任务的提交和执行。
在主函数中,提交了10个任务,每个任务都是一个简单的打印数字的函数,最后等待所有任务执行完毕后销毁线程池。
*/

java 实现线程安全的单例模式

一、平时使用的软件中,例如 回收站、线程池、文件系统等,都只有一个实例,这些都是单例模式的典型应用。

  单例模式:确保某个类只有一个实例,并提供一个全局访问点来访问这个实例。

  单例模式有三个要点:

    1. 某个类只能有一个实例

    2. 必须自行创建这个实例

    3. 必须自行向整个系统提供这个实例。

  以上三个要点提示着我们的代码编写需要注意,构造函数必须私有,否则在其他类中便可以调用构造函数创建实例,难以保证实例的唯一性。

二、单例模式分为饿汉模式和懒汉模式

//饿汉模式:(线程安全)
public class Singleton1 {
//    静态私有成员变量
    private static Singleton1 instance = new Singleton1();
//    私有构造函数
    private Singleton1() {
    }
//    静态公有工厂方法,返回唯一实例
    public static Singleton1 getInstance() {
        return instance;
    }
}

 

// 懒汉模式:(线程不安全,需要通过双重检查锁定机制控制)
public class Singleton2 {
//    静态私有成员变量
    private static Singleton2 instance = null;
//    私有构造函数
    private Singleton2() {
    }
//    静态公有工厂方法,判断成员变量是否为空,不为空则实例化
    public static Singleton2 getInstance() {
        if(instance == null)
            instance = new Singleton2();
        return instance;
    }
}

  优缺点:

   饿汉模式不需要考虑线程安全问题,调用速度和访问速度优于懒汉模式,但是由于它不管是否被调用都会提前创建类的实例,所以资源利用效率比较低,系统加载时间比较长。

懒汉模式实现了延迟加载,但是需要克服多个线程同时访问的问题,需要通过双重检查锁定机制进行控制,导致系统性能受到一定影响。

三、下面两个方法实现懒汉模式的线程安全。

  为什么会线程不安全?

   假设有两个线程 A B,其中 A 执行到检查方法,即 if(instance == null) 前,实例都没有被创建,那么 A 会得到 ture 的结果,但是此时调度算法选择 B 线程运行,那么当 B 执行 到 if(instance == null) 时得到的也是 true,那这就很尴尬了,两个线程都会执行 instance = new Singleton2(); 从而创建了两个实例。

  1.双重检查锁定机制。

为了避免以上这种尴尬的情况,需要将这两行代码加上同步锁。但这还不够完美,每次调用函数得到实例都要试图加上一个同步锁,而加锁是一个非常耗时的操作,没有必要的情况下应该尽量避免。基于这种想法,我们可以在加锁前再次判断实例是否为空。这就是双重检查锁定机制。

public class Singleton3 {
//    私有静态成员变量
    private static Singleton3 instance = null;
//    私有构造函数
    private Singleton3() {
    }
//    共有静态工厂方法
    public static Singleton3 getInstance() {
//        判断 instance 是否为空,为空->加锁,创建实例(为了进程安全,再次判断),不为空->返回实例
        if(instance == null) {
            synchronized (Singleton3.class) {
                if(instance == null)
                    instance = new Singleton3();
            }
        }
        return instance;
    }
}

  2. 使用静态内部类创建实例 。JAVA 语言中最好的实现方法)

public class Singleton4 {
//    私有构造函数
    private Singleton4() {
    }
//    静态内部类
    private static class HolderClass{
        private static final Singleton4 instance = new Singleton4();
    }
//    静态公有工厂方法,返回内部类中创建的实例
    public static Singleton4 getInstance() {
        return HolderClass.instance;
    }
}

  当装载 Singleton4 类时,instance 不一定被初始化,因为它是内部类的成员变量。Singleton4 没有被主动使用,只有调用 getInstance 方法时,才会装载 Singleton4 类,从而实例化 instance

使用静态内部类很好得在实现了延迟加载的同时,保证初始化 instance 时只有一个线程。

 

以上是关于C语言-单例模式实现线程池的主要内容,如果未能解决你的问题,请参考以下文章

嵌入式C语言设计模式 --- 单例模式

单例设计模式原理详解Java/JS/Go/Python/TS不同语言实现

设计模式--单例模式

C++之特殊类的设计(单例模式)

C++之特殊类的设计(单例模式)

C++ 单例模式(singleton)