Zephyr condvar

简介

  • condvar 是一种同步原语,它允许线程在继续之前等待某个条件变为 true。
  • condvar 功能与event有一些相似,event 可以等待一个或者多个条件满足,而一个条件变量只能等待一个条件满足。
  • 根据他们之间的差别,选择出适合应用场景的同步对象。

数据结构

struct k_condvar {
	_wait_q_t wait_q;
};
  • 在 k_condvar 中仅包含一个等待队列,用于保存需要被挂起的线程。
  • 对于一个IPC对象而言,包含等待队列几乎是必须的,从这一点来看,条件变量比其他IPC对象的功能更加简单,占用的内存空间更少。
  • 例如 event 它需要等待多个事件,而每一个事件不可能同时满足,因此需要使用变量保存已经发生的事件,在访问该变量时需要做到互斥,如果使用全局的自旋锁,程序的运行效率会大大降低,因此每一个events 还需要有一个独立的自旋锁。
  • condvar 没有保存事件的变量,因此函数一旦调用 k_condvar_wait 肯定会被挂起,直到下一次执行 k_condvar_signal 或者 k_condvar_broadcast,被挂起的线程才能继续运行。

condvar 初始化

#define Z_CONDVAR_INITIALIZER(obj)                                             \
	{                                                                          \
		.wait_q = Z_WAIT_Q_INIT(&obj.wait_q),                                  \
	}

int z_impl_k_condvar_init(struct k_condvar *condvar)
{
	z_waitq_init(&condvar->wait_q);
	z_object_init(condvar);

	SYS_PORT_TRACING_OBJ_INIT(k_condvar, condvar, 0);

	return 0;
}
  • 在condvar 的数据结构中只包含一个等待队列,因此初始化函数的功能便是将队列重设为初始状态。

等待条件

int k_condvar_wait(struct k_condvar *condvar, struct k_mutex *mutex, k_timeout_t timeout)

  • k_condvar_wait 函数用于等待条件变量的唤醒,并使用 mutex 同步访问共享资源。
  • 它释放 mutex,以允许其他线程访问共享资源,然后重新获取 mutex,以便在条件变量被唤醒或超时时保护共享资源。
int z_impl_k_condvar_wait(struct k_condvar *condvar, struct k_mutex *mutex,
			  k_timeout_t timeout)
{
	k_spinlock_key_t key;
	int ret;

	SYS_PORT_TRACING_OBJ_FUNC_ENTER(k_condvar, wait, condvar);

	key = k_spin_lock(&lock);
	k_mutex_unlock(mutex);

	ret = z_pend_curr(&lock, key, &condvar->wait_q, timeout);
	k_mutex_lock(mutex, K_FOREVER);

	SYS_PORT_TRACING_OBJ_FUNC_EXIT(k_condvar, wait, condvar, ret);

	return ret;
}

发布信号

k_condvar_signal(struct k_condvar *condvar)

  • k_condvar_signal 用于唤醒一个线程,在挂起时线程是根据优先级进行排列的,因此被唤醒的线程是队列中优先级最高的线程。
int z_impl_k_condvar_signal(struct k_condvar *condvar)
{
	k_spinlock_key_t key = k_spin_lock(&lock);

	SYS_PORT_TRACING_OBJ_FUNC_ENTER(k_condvar, signal, condvar);

	struct k_thread *thread = z_unpend_first_thread(&condvar->wait_q);

	if (thread != NULL) {
		SYS_PORT_TRACING_OBJ_FUNC_BLOCKING(k_condvar, signal, condvar, K_FOREVER);

		arch_thread_return_value_set(thread, 0);
		z_ready_thread(thread);
		z_reschedule(&lock, key);
	} else {
		k_spin_unlock(&lock, key);
	}

	SYS_PORT_TRACING_OBJ_FUNC_EXIT(k_condvar, signal, condvar, 0);

	return 0;
}

发布广播

int k_condvar_broadcast(struct k_condvar *condvar)

  • k_condvar_signal 用于唤醒所有等待的线程。
int z_impl_k_condvar_broadcast(struct k_condvar *condvar)
{
	struct k_thread *pending_thread;
	k_spinlock_key_t key;
	int woken = 0;

	key = k_spin_lock(&lock);

	SYS_PORT_TRACING_OBJ_FUNC_ENTER(k_condvar, broadcast, condvar);

	/* wake up any threads that are waiting to write */
	while ((pending_thread = z_unpend_first_thread(&condvar->wait_q)) !=
	       NULL) {
		woken++;
		arch_thread_return_value_set(pending_thread, 0);
		z_ready_thread(pending_thread);
	}

	SYS_PORT_TRACING_OBJ_FUNC_EXIT(k_condvar, broadcast, condvar, woken);

	z_reschedule(&lock, key);

	return woken;
}

使用示例

  • 下面是一个生产者消费者使用实例,消费者在生产者生产完成之前需要进入等待,生产完成之后再通过条件变量唤醒消费线程运行。
  • 两个线程使用了共享资源 buffer_count 和 buffer,在访问时需要进行加锁,消费者进入等待时需要释放锁,被唤醒后重新获取锁。
#define BUFFER_SIZE 10

static char buffer[BUFFER_SIZE];
static int buffer_count = 0;

static struct k_mutex buffer_mutex;
static struct k_condvar buffer_condvar;

void producer_thread(void *arg)
{
    while (1) {
        /* Produce data and add it to the buffer */
        char data = 'A' + (rand() % 26);
        k_mutex_lock(&buffer_mutex, K_FOREVER);
        while (buffer_count == BUFFER_SIZE) {
            /* Buffer is full, wait for consumer to consume data */
            k_condvar_wait(&buffer_condvar, &buffer_mutex, K_FOREVER);
        }
        buffer[buffer_count++] = data;
        k_condvar_signal(&buffer_condvar);
        k_mutex_unlock(&buffer_mutex);
        k_sleep(K_MSEC(1000));
    }
}

void consumer_thread(void *arg)
{
    while (1) {
        k_mutex_lock(&buffer_mutex, K_FOREVER);
        while (buffer_count == 0) {
            /* Buffer is empty, wait for producer to add data */
            k_condvar_wait(&buffer_condvar, &buffer_mutex, K_FOREVER);
        }
        char data = buffer[--buffer_count];
        k_condvar_signal(&buffer_condvar);
        k_mutex_unlock(&buffer_mutex);
        /* Consume data */
        printk("Consumed data: %c\n", data);
        k_sleep(K_MSEC(1000));
    }
}

void main(void)
{
    k_mutex_init(&buffer_mutex);
    k_condvar_init(&buffer_condvar);
    k_thread_create(&producer_thread_data, producer_thread_stack,
                    K_THREAD_STACK_SIZEOF(producer_thread_stack),
                    producer_thread, NULL, NULL, NULL,
                    1, 0, K_NO_WAIT);
    k_thread_create(&consumer_thread_data, consumer_thread_stack,
                    K_THREAD_STACK_SIZEOF(consumer_thread_stack),
                    consumer_thread, NULL, NULL, NULL,
                    1, 0, K_NO_WAIT);
}

总结

  • condvar 是一种轻量级的同步对象,可以让程序在某个条件满足之前进入等待,当条件满足之后再继续运行。
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

咕咚.萌西

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值