SDL中的互斥量和条件变量

来源:互联网 发布:mysql 最多从库 编辑:程序博客网 时间:2024/06/15 21:40

一、概述

       在最简单的音视频播放器中,由于解复用和解码是在不同的线程中,存放包的队列是公共资源,需要互斥。解复用向队列添加包,解码从队列取包,也需要同步。所以队列的入队和出队操作,采用了互斥量和条件变量。

二、主要内容

互斥量   

  1、创建互斥量 

SDL_mutex *mutex;mutex = SDL_CreateMutex();
     创建的互斥量默认是未上锁的。    

 2、上锁和解锁

if (SDL_LockMutex(mutex) == 0) {  /* Do stuff while mutex is locked */  SDL_UnlockMutex(mutex);} else {  fprintf(stderr, "Couldn't lock mutex\n");}
   成功返回0,否则返回<0

3、销毁锁

SDL_DestroyMutex(mutex);

条件变量

1、创建条件变量

SDL_cond* SDL_CreateCond(void)

2、等待条件变量

int SDL_CondWait(SDL_cond*  cond,                 SDL_mutex* mutex)

信号激活后,返回0,否则返回错误代码。

SDL_ConWait必须在互斥量锁住之后才能调用。该函数会解锁锁住的互斥量,并等待拥有该锁的线程激活信号。激活后,会重新上锁。

3、激活信号

int SDL_CondSignal(SDL_cond* cond)
成功返回 0,否则返回错误代码。

SDL_ConSignal会激活等待的一个线程(根据优先级),而不是所有等待的线程。
4、销毁条件变量

void SDL_DestroyCond(SDL_cond* cond)

三、一个简单的例子

生产者向队列不断的放入产品,消费者不停的从队列取出产品进行消费。这里不考虑队列满的情况,假设队列容量是无限大。当没有产品时,消费者等待。

<pre name="code" class="cpp">extern"C"{#include <libavcodec/avcodec.h>#include <libavformat/avformat.h>#include <libswscale/swscale.h>#include "include/sdl/SDL.h"#include "include/sdl/SDL_thread.h"#include "include/libavutil/time.h"#include "include/libavutil/avstring.h"#include "libswresample/swresample.h"}#pragma comment(lib, "lib/avformat.lib")#pragma comment(lib, "lib/avcodec.lib")#pragma comment(lib, "lib/avutil.lib")#pragma comment(lib, "lib/swscale.lib")#pragma comment(lib, "lib/swresample.lib")#pragma comment(lib, "lib/SDL.lib")#pragma comment(lib, "lib/SDLmain.lib")#include <stdio.h>#include<stdlib.h>#include<string.h>#include <assert.h>/*包队列*/typedef struct PacketQueue {AVPacketList *first_pkt, *last_pkt;int nb_packets;//包个数int size;//包大小SDL_mutex *mutex;//互斥量SDL_cond *cond;//条件量} PacketQueue;/*初始化队列*/void packet_queue_init(PacketQueue *q) {memset(q, 0, sizeof(PacketQueue));q->mutex = SDL_CreateMutex();q->cond = SDL_CreateCond();}/*入队操作*/int packet_queue_put(PacketQueue *q, AVPacket *pkt) {AVPacketList *pkt1;if (av_dup_packet(pkt) < 0) {return -1;}pkt1 = (AVPacketList*)av_malloc(sizeof(AVPacketList));if (!pkt1)return -1;pkt1->pkt = *pkt;pkt1->next = NULL;SDL_LockMutex(q->mutex);if (!q->last_pkt)q->first_pkt = pkt1;elseq->last_pkt->next = pkt1;q->last_pkt = pkt1;q->nb_packets++;q->size += pkt1->pkt.size;SDL_CondSignal(q->cond);SDL_UnlockMutex(q->mutex);return 0;}/*出队操作*/int packet_queue_get(PacketQueue *q, AVPacket *pkt, int block){AVPacketList *pkt1;int ret;SDL_LockMutex(q->mutex);for (;;) {pkt1 = q->first_pkt;if (pkt1) {q->first_pkt = pkt1->next;if (!q->first_pkt)q->last_pkt = NULL;q->nb_packets--;q->size -= pkt1->pkt.size;*pkt = pkt1->pkt;av_free(pkt1);ret = 1;break;}else if (!block) {ret = 0;break;}else {SDL_CondWait(q->cond, q->mutex);}}SDL_UnlockMutex(q->mutex);return ret;}//生产线程int produce(void * qq){PacketQueue *q = (PacketQueue*)qq;while (1){AVPacket* pkt = (AVPacket*)malloc(sizeof(AVPacket));if (packet_queue_put(q, pkt)==0){printf("produce %d product!\n",q->nb_packets);} SDL_Delay(2);//等待2ms}return 0;}//消费线程int consume(void * qq){PacketQueue *q = (PacketQueue*)qq;while (1){int ret = packet_queue_get(q, pkt, 0);<span style="white-space:pre"></span>if (ret ==1)<span style="white-space:pre"></span>{<span style="white-space:pre"></span>printf("consume a product\n");<span style="white-space:pre"></span>}<span style="white-space:pre"></span>else if (ret == 0)<span style="white-space:pre"></span>{<span style="white-space:pre"></span>printf("no product ,wait!\n");<span style="white-space:pre"></span>}<span style="white-space:pre"></span><span style="white-space:pre"></span>SDL_Delay(1);}return 0;}int main(int argc, char *argv[]) {SDL_Thread * threadA;SDL_Thread * threadB;PacketQueue * q = (PacketQueue*)malloc(sizeof(PacketQueue));        packet_queue_init(q);threadA = SDL_CreateThread(produce, q);threadB = SDL_CreateThread(consume, q);SDL_Delay(500);SDL_KillThread(threadA);SDL_KillThread(threadB);return 0;}


运行结果:



从图中可以看到,当生产者向队列放入1产品后,消费者消费了一个产品。当消费者再次取产品的时候,发现没有产品,所以等待。
在程序中,我让生产者线程生产的速度低于消费的速度(一个delay了2毫秒,一个delay了1毫秒),所以才会出现消费者等待的情形。

现在假设队列的容量为3,并当队列满的时候,不允许再向队列放入产品,等待消费者消费了之后再放入产品。实现起来也不难,只需要在入队时做个判断就可以了。源代码如下:

extern"C"{#include <libavcodec/avcodec.h>#include <libavformat/avformat.h>#include <libswscale/swscale.h>#include "include/sdl/SDL.h"#include "include/sdl/SDL_thread.h"#include "include/libavutil/time.h"#include "include/libavutil/avstring.h"#include "libswresample/swresample.h"}#pragma comment(lib, "lib/avformat.lib")#pragma comment(lib, "lib/avcodec.lib")#pragma comment(lib, "lib/avutil.lib")#pragma comment(lib, "lib/swscale.lib")#pragma comment(lib, "lib/swresample.lib")#pragma comment(lib, "lib/SDL.lib")#pragma comment(lib, "lib/SDLmain.lib")#include <stdio.h>#include<stdlib.h>#include<string.h>#include <assert.h>/*包队列*/typedef struct PacketQueue {AVPacketList *first_pkt, *last_pkt;int nb_packets;//包个数int size;//包大小SDL_mutex *mutex;//互斥量SDL_cond *cond;//条件量int max_packets;} PacketQueue;void packet_queue_init(PacketQueue *q) {memset(q, 0, sizeof(PacketQueue));q->max_packets = 3;//设置队列容量q->mutex = SDL_CreateMutex();q->cond = SDL_CreateCond();}int packet_queue_put(PacketQueue *q, AVPacket *pkt) {if (q->nb_packets>=q->max_packets)//队列满则返回{return 1;}AVPacketList *pkt1;if (av_dup_packet(pkt) < 0) {return -1;}pkt1 = (AVPacketList*)av_malloc(sizeof(AVPacketList));if (!pkt1)return -1;pkt1->pkt = *pkt;pkt1->next = NULL;SDL_LockMutex(q->mutex);if (!q->last_pkt)q->first_pkt = pkt1;elseq->last_pkt->next = pkt1;q->last_pkt = pkt1;q->nb_packets++;q->size += pkt1->pkt.size;SDL_CondSignal(q->cond);SDL_UnlockMutex(q->mutex);return 0;}int packet_queue_get(PacketQueue *q, AVPacket *pkt, int block){AVPacketList *pkt1;int ret;SDL_LockMutex(q->mutex);for (;;) {pkt1 = q->first_pkt;if (pkt1) {q->first_pkt = pkt1->next;if (!q->first_pkt)q->last_pkt = NULL;q->nb_packets--;q->size -= pkt1->pkt.size;*pkt = pkt1->pkt;av_free(pkt1);ret = 1;break;}else if (!block) {ret = 0;break;}else {SDL_CondWait(q->cond, q->mutex);}}SDL_UnlockMutex(q->mutex);return ret;}int produce(void * qq){PacketQueue *q = (PacketQueue*)qq;while (1){AVPacket* pkt = (AVPacket*)malloc(sizeof(AVPacket));int ret = packet_queue_put(q, pkt);if (ret==0){printf("produce %dth product!\n",q->nb_packets);} else if (ret==1){printf("Queue is full,wait!\n");SDL_Delay(1);}}return 0;}int consume(void * qq){PacketQueue *q = (PacketQueue*)qq;while (1){AVPacket* pkt = (AVPacket*)malloc(sizeof(AVPacket));int ret = packet_queue_get(q, pkt, 0);if (ret ==1){printf("consume a product\n");}else if (ret == 0){printf("no product ,wait!\n");SDL_Delay(1);}}return 0;}int main(int argc, char *argv[]) {SDL_Thread * threadA;SDL_Thread * threadB;PacketQueue * q = (PacketQueue*)malloc(sizeof(PacketQueue));    packet_queue_init(q);threadA = SDL_CreateThread(produce, q);threadB = SDL_CreateThread(consume, q);SDL_Delay(500);SDL_KillThread(threadA);SDL_KillThread(threadB);return 0;}

运行结果:



0 0