1.背景
最近准备通过多线程申请和释放内存,测试多核并发的场景,之前的用例两个线程是不同优先级的,现在设计的新用例是相同优先级,希望他们自己去申请和释放。由于我还比较菜鸟,第一版的代码写的很简陋,纯新建了四个线程,相同优先级,两个申请两个释放,跑起来会出现释放的线程一下子全运行完了,后面才开始申请内存。
丑陋的源代码:
/* 线程1入口 */
static rt_void_t thread1_mp_alloc(jx_void_t *parameter)
{
jx_mw_mb_handle_t* handle = (jx_mw_mb_handle_t*)parameter;
int i;
for (i = 0 ; i < 200 ; i++)
{
rt_kprintf("thread1 try to alloc block\n");
if (ptr[i] == JX_NULL)
{
if (jx_mw_mb_alloc(handle, 1920*1080, &ptr[i], JX_NULL) != JX_OK) {
rt_kprintf("jx_mw_mb_alloc failed\n");
}
if (ptr[i] != JX_NULL)
rt_kprintf("allocate No.%d, addr: 0x%08x\n", i, (unsigned int)ptr[i]);
}
}
}
/* 线程2入口*/
static jx_void_t thread2_mp_release(jx_void_t *parameter)
{
jx_thread_sleep_ms(1000);
jx_mw_mb_handle_t* handle = (jx_mw_mb_handle_t*)parameter;
int i;
for (i = 0; i < 200 ; i++)
{
rt_kprintf("%uthread2 try to release block\n",i);
/* 释放所有分配成功的内存块 */
if (ptr[i] != JX_NULL)
{
rt_kprintf("release block %d\n", i);
if (jx_mw_mb_free(handle, ptr[i], JX_NULL) != JX_OK) {
rt_kprintf("jx_mw_mb_free failed\n");
}
ptr[i] = JX_NULL;
}
}
}
jx_void_t mb_func_test_smp1()
{
jx_err_t err;
jx_mw_mb_conf_t config;
memset(&config, 0, sizeof(config));
strncpy(config.name, "isp", sizeof(config.name) - 1);
config.max_pool_count = 1;
config.mb_pools[0].block_size = 1920*1080;
config.mb_pools[0].block_count = 4;
if ((err = jx_mw_mb_set_conf(&config)) != JX_OK) {
// rt_kprintf("jx_mw_mb_set_conf failed err = %lu\n", err);
return;
}
jx_mw_mb_handle_t* handle = JX_NULL;
if((err = jx_mw_mb_init(&handle, "isp")) != JX_OK) {
// rt_kprintf("jx_mw_mb_init failed, err = %ld\n", err);
return;
}
// list_thread();
/* 创建线程1:申请内存池 */
tid1 = rt_thread_create("thread1", thread1_mp_alloc, handle,
THREAD_STACK_SIZE,
THREAD_PRIORITY, THREAD_TIMESLICE);
if (tid1 != JX_NULL)
rt_thread_startup(tid1);
/* 创建线程2:释放内存池*/
tid2 = rt_thread_create("thread2", thread2_mp_release, handle,
THREAD_STACK_SIZE,
THREAD_PRIORITY, THREAD_TIMESLICE);
if (tid2 != JX_NULL)
rt_thread_startup(tid2);
/* 创建线程3:申请内存池 */
tid3 = rt_thread_create("thread3", thread3_mp_alloc, handle,
THREAD_STACK_SIZE,
THREAD_PRIORITY, THREAD_TIMESLICE);
if (tid3 != JX_NULL)
rt_thread_startup(tid3);
list_thread();
/* 创建线程4:释放内存池*/
tid4 = rt_thread_create("thread4", thread4_mp_release, handle,
THREAD_STACK_SIZE,
THREAD_PRIORITY, THREAD_TIMESLICE);
if (tid4 != JX_NULL)
rt_thread_startup(tid4);
list_thread();
// while(1){
// jx_thread_sleep_ms(100000);
// }
}
2. 优化----增加信号量
我想着不能这么随意,这样运行跑不出效果。于是决定增加信号量。
信号量简介
信号量(semaphore)是一种用于多线程同步的机制。信号量维护一个计数器,表示可以访问资源的线程数量。线程可以对信号量进行两种操作:
- P操作(wait, take) :等待信号量计数器大于零,然后将计数器减一。如果计数器为零,线程将阻塞直到计数器大于零。
- V操作(signal, release) :将信号量计数器加一,并唤醒等待在该信号量上的线程(如果有的话)。
在代码中的应用
在代码中,我们使用了两个信号量sem_alloc和sem_release,分别用于控制内存池的申请和释放操作。具体来说:
sem_alloc:控制申请内存池的线程。sem_release:控制释放内存池的线程。
线程与信号量的交互
- 初始状态:
-
sem_alloc的初始值为1,表示可以有一个线程进行内存池申请操作。sem_release的初始值为0,表示没有线程可以进行内存池释放操作,因为还没有任何内存块被申请。
- 线程1和线程3(申请内存池) :
-
- 这两个线程都会在
sem_alloc信号量上进行take操作(P操作),等待计数器大于零。 - 当
sem_alloc的计数器大于零时,线程1或线程3会被唤醒,并将计数器减一。 - 申请内存池操作完成后,这些线程会对
sem_release信号量进行release操作(V操作),将计数器加一,允许释放内存池的线程运行。
- 这两个线程都会在
- 线程2和线程4(释放内存池) :
-
- 这两个线程会在
sem_release信号量上进行take操作,等待计数器大于零。 - 当
sem_release的计数器大于零时,线程2或线程4会被唤醒,并将计数器减一。 - 释放内存池操作完成后,这些线程会对
sem_alloc信号量进行release操作,将计数器加一,允许申请内存池的线程运行。
- 这两个线程会在
同步过程详解
- 初始阶段:
-
sem_alloc= 1,sem_release= 0。- 线程1或线程3首先执行,成功申请内存池后,
sem_alloc= 0,sem_release= 1。
- 释放内存池:
-
- 线程2或线程4被唤醒,执行释放内存池操作后,
sem_alloc= 1,sem_release= 0。 - 释放操作完成,唤醒等待在
sem_alloc上的线程(线程1或线程3)。
- 线程2或线程4被唤醒,执行释放内存池操作后,
- 交替执行:
-
- 这个过程交替进行,确保申请和释放操作不会同时进行,从而避免竞争条件和资源冲突。
通过使用信号量,线程间可以实现精确的同步,保证操作的有序进行。
修改方法:增加线程间的同步
修改后代码:
#include <rtthread.h>
#include <string.h>
#define THREAD_STACK_SIZE 1024
#define THREAD_PRIORITY 25
#define THREAD_TIMESLICE 10
/* 信号量 */
static rt_sem_t sem_alloc;
static rt_sem_t sem_release;
void thread1_mp_alloc(void* parameter)
{
jx_mw_mb_handle_t* handle = (jx_mw_mb_handle_t*)parameter;
while (1)
{
/* 等待申请信号量 */
rt_sem_take(sem_alloc, RT_WAITING_FOREVER);
/* 申请内存池 */
/* 释放释放信号量 */
rt_sem_release(sem_release);
}
}
void thread2_mp_release(void* parameter)
{
jx_mw_mb_handle_t* handle = (jx_mw_mb_handle_t*)parameter;
while (1)
{
/* 等待释放信号量 */
rt_sem_take(sem_release, RT_WAITING_FOREVER);
/* 释放内存池 */
/* 释放申请信号量 */
rt_sem_release(sem_alloc);
}
}
void thread3_mp_alloc(void* parameter)
{
jx_mw_mb_handle_t* handle = (jx_mw_mb_handle_t*)parameter;
while (1)
{
/* 等待申请信号量 */
rt_sem_take(sem_alloc, RT_WAITING_FOREVER);
/* 申请内存池的操作 */
/* 释放释放信号量 */
rt_sem_release(sem_release);
}
}
void thread4_mp_release(void* parameter)
{
jx_mw_mb_handle_t* handle = (jx_mw_mb_handle_t*)parameter;
while (1)
{
/* 等待释放信号量 */
rt_sem_take(sem_release, RT_WAITING_FOREVER);
/* 释放内存池的操作 */
/* 释放申请信号量 */
rt_sem_release(sem_alloc);
}
}
jx_void_t mb_func_test_smp1()
{
jx_err_t err;
jx_mw_mb_conf_t config;
memset(&config, 0, sizeof(config));
strncpy(config.name, "isp", sizeof(config.name) - 1);
config.max_pool_count = 1;
config.mb_pools[0].block_size = 1920*1080;
config.mb_pools[0].block_count = 4;
if ((err = jx_mw_mb_set_conf(&config)) != JX_OK) {
return;
}
jx_mw_mb_handle_t* handle = JX_NULL;
if((err = jx_mw_mb_init(&handle, "isp")) != JX_OK) {
return;
}
/* 初始化信号量 */
sem_alloc = rt_sem_create("sem_alloc", 1, RT_IPC_FLAG_FIFO);
sem_release = rt_sem_create("sem_release", 0, RT_IPC_FLAG_FIFO);
if (sem_alloc == JX_NULL || sem_release == JX_NULL) {
return;
}
/* 创建线程1:申请内存池 */
rt_thread_t tid1 = rt_thread_create("thread1", thread1_mp_alloc, handle,
THREAD_STACK_SIZE,
THREAD_PRIORITY, THREAD_TIMESLICE);
if (tid1 != JX_NULL)
rt_thread_startup(tid1);
/* 创建线程2:释放内存池*/
rt_thread_t tid2 = rt_thread_create("thread2", thread2_mp_release, handle,
THREAD_STACK_SIZE,
THREAD_PRIORITY, THREAD_TIMESLICE);
if (tid2 != JX_NULL)
rt_thread_startup(tid2);
/* 创建线程3:申请内存池 */
rt_thread_t tid3 = rt_thread_create("thread3", thread3_mp_alloc, handle,
THREAD_STACK_SIZE,
THREAD_PRIORITY, THREAD_TIMESLICE);
if (tid3 != JX_NULL)
rt_thread_startup(tid3);
/* 创建线程4:释放内存池*/
rt_thread_t tid4 = rt_thread_create("thread4", thread4_mp_release, handle,
THREAD_STACK_SIZE,
THREAD_PRIORITY, THREAD_TIMESLICE);
if (tid4 != JX_NULL)
rt_thread_startup(tid4);
list_thread();
/*清除信号量*/
rt_sem_delete(sem_alloc);
rt_sem_delete(sem_release);
}