今天将向大家介绍一下SDL中的多线程的使用。通过下面对SDL 线程与锁相关的API介绍,你会发现,它与 Linux, Windows相关的API几乎是一模一样的。从这里可以推断出,其实SDL对于多线程的处理只是为大家提供了一套统一接口,并没有做其它太多的工作。
这是我们介绍 SDL 的第六篇文章。
为啥要用多线程?
我觉得这个小节的标题就是一个废话。不过为了文章的完整性,还是简单的说一说吧。多线程(多进程)是啥意思呢?做个不恰当的比喻,可以把CPU看成是孙悟空,它有一个能耐,从后脑揪几个猴毛就可以变出许多的小猴子。
多线程(多进程)就是这些小猴子。当干一件比较复杂的事儿时,可以孙悟空一个人干,这样自己比较累。它还有一种选择就是揪几根猴毛,让小猴子们一起帮着干。这样一件复杂的事件,分给许多猴子干,每只猴只干一部分,事情很快就被做完了,这样岂不是比一个人干要强的多?
当然,有好处也有坏外。猴子多了就需要管理,如果管理不好,就会闹翻天。比如,只有一块肉,该给哪个猴子吃呢?这真是一个另人头痛的问题。
实际上整个操作系统的演进,就是一部管理学的演进。如何才能让CPU,内存,磁盘I/O,各种设备之间高效的工作,一直是操作系统追求的目标。当然,这话有点扯远了。
今天我们要讲的就是多线程(多进程)之间该如何高效的工作。要想让多线程之间高效工作,就要给它们之间立点规矩,大家都要遵守的规矩。
线程互斥与同步
当僧多粥少时,就引入了互斥的概念。再举个我们生活中的例子吧,比如有一大家族住在同一个大屋子里,却只有一个厕所。早上起来大家都想去厕所,这时有谁先抢到了厕所,其它人就只能等他出来后再进入了,这就是互斥。
当仅有一份资源,大家都需要时,这就产生了管理问题。解决的办法就是通过互斥方法来解决。这种情况是在做多线程处理时要尽量避免的;如果资源足够呢?那当然是平均分配,人人有份了。这中情况是多线路程最希望的。
除了互斥之外,有些情况还需要更精细化的管理,比如说同步 。例如车间里的流水线,每个人负责一块,每一块都是半成品,第一个人完成之后交给第二个人做下一步,而后面的人又必须依赖于前而人的结果,依次类推,最后一个人才能完成最终的产品。这就是线程间的精细化管理同步。
要想实现互斥和同步,就需要一种机制。在操作系统上提供了锁的概念来达到互斥与同步。
锁的种类
在操作系统上有很种锁,有读写锁、自旋锁、可重入锁等。下面我简单的介绍一下它们之间的不同。
读写锁: 分为读锁与写锁。所谓读锁就是被访问的资源只要你不改变它的值,你就可以访问,但如果你想改变它,那么就需要等所有读它的线程都释放了它们的锁后,才可以进行修改;写锁是同一时刻只能有一个人访问,当资源被加锁后,其它人只能等待。
自旋锁: 偿试着给访问资源加锁,如果此时被访问资源已经上锁了,那就一直不停的偿试,直到加锁成功为止。由于它会非常消耗CPU资源,所以一般只锁今资源非常短的情况下才能使用它。
可重入锁: 同一个线程对被访问资源可以一直加锁。但如果被访问资源已经上锁了,那么其它线程则无法对其加锁。
锁是解决互斥的一种好办法,但同样有利必有弊。如果使用不善就会出现死锁。
死锁问题
死锁顾名思意,就是打不开的锁。它是怎么产生的呢?举个例子,两个人需要一起完成一件事儿,A说他要等B做完了,他才能开始;而B说它要等A做完了,它才能开使。于时他们在相互等待中老去。
看类很简单的问题,但这类事情经常在我们的工作中出现。而在我们开发的多线程程序中更是频繁出现。别说人没遇到过哟!
如何解决?那就是考验你的管理能力了。共实很多情况是出现了死锁我们自己却不知道,否则的话,凭我们的聪名才智怎么能让他们一直锁在那儿呢。
SDL多线程
上面介绍了一大堆的理论,现在来看看 SDL 为我们都提供了那些API吧。
-
创建线程
SDL_Thread* SDL_CreateThread(SDL_ThreadFunction fn,
const char* name,
void* data) -
fn: 线程要运行的函数。
-
name: 线程名。
-
data: 函数参数。
等待线程
void SDL_WaitThread(SDL_Thread* thread,
int* status)
-
等待线程结束。
-
创建互斥量
SDL_mutex* SDL_CreateMutex(void)
-
也就是创建一个稀有资源,这样大家就去抢这个资源。从而达到为真正资源加锁的目的。
-
销毁互斥量
void SDL_DestroyMutex(SDL_mutex* mutex)
加锁
int SDL_LockMutex(SDL_mutex* mutex)
解锁
int SDL_UnlockMutex(SDL_mutex* mutex)
常用的与线程和锁相关的 API 就以上几个,是不是非常简单?下面我们来看一个简单的例子吧。
例子
下面这个例子是在主线程中创建了一个子线程。然后主线程就一直等待子线程结束。等子线程结束后,主线程也随之结束。
#include <stdio.h>
#include "SDL.h"
/* Very simple thread - counts 0 to 9 delaying 50ms between increments */
static int TestThread(void *ptr)
{
int cnt;
for (cnt = 0; cnt < 10; ++cnt) {
printf("\nThread counter: %d", cnt);
SDL_Delay(50);
}
return cnt;
}
int main(int argc, char *argv[])
{
SDL_Thread *thread;
int threadReturnValue;
printf("\nSimple SDL_CreateThread test:");
/* Simply create a thread */
thread = SDL_CreateThread(TestThread, "TestThread", (void *)NULL);
if (NULL == thread) {
printf("\nSDL_CreateThread failed: %s\n", SDL_GetError());
} else {
SDL_WaitThread(thread, &threadReturnValue);
printf("\nThread returned value: %d", threadReturnValue);
}
return 0;
}
小结
本文主要介绍了两方面的内容。一是对多线程理论做了一下简单的介绍;二是介绍了SDL中与线程和锁相关的API。
最后通过一个例子显示了如何使用 SDL 中的多线程。
粉丝福利, 免费领取C++音视频学习资料包+学习路线大纲、技术视频/代码,内容包括(音视频开发,面试题,FFmpeg ,webRTC ,rtmp ,hls ,rtsp ,ffplay ,编解码,推拉流,srs)↓↓↓↓↓↓见下面↓↓文章底部点击免费领取↓↓