我目前正在使用Allegro跨平台库开始在C中的独立游戏开发。我认为我会将输入,声音,游戏引擎和图形等内容分离到各自独立的线程中,以提高程序的稳健性。没有任何多线程经验,我的问题是:
如果我在内存中有一段数据(例如,指向数据结构的指针),一个线程是否可以随意写入,而另一个线程可以随意读取,或者每个线程都必须使用互斥锁来锁定内存,然后读或写,然后解锁?
特别是,我在考虑游戏引擎和视频渲染器之间的交互。 (这是2D。)我的计划是让引擎处理用户输入,然后吐出适当的音频和视频以馈送到扬声器和监视器。我以为我有一个全局指针指向要在屏幕上绘制的下一个位图,游戏引擎和渲染器的代码将是这样的:
ALLEGRO_BITMAP *nextBitmap;
boolean using;
void GameEngine ()
{
ALLEGRO_BITMAP *oldBitmap;
while (ContinueGameEngine())
{
ALLEGRO_BITMAP *bitmap = al_create_bitmap (width, height);
MakeTheBitmap (bitmap);
while (using) ; //The other thread is using the bitmap. Don't mess with it!
al_destroy_bitmap (nextBitmap);
nextBitmap = bitmap;
}
}
void Renderer ()
{
while (ContinueRenderer())
{
ALLEGRO_BITMAP *bitmap = al_clone_bitmap (nextBitmap);
DrawBitmapOnScreen (bitmap);
}
}
这似乎不稳定......可能会在调用al_clone_bitmap
时发生某些事情,但我不太确定如何处理这样的事情。我会在位图上使用互斥锁,但互斥锁似乎需要时间来锁定和解锁,我希望这两个线程(尤其是游戏引擎线程)尽可能快地运行。我还读了一个叫做条件的东西,但我完全不知道条件是如何适用或有用的,尽管我确定它们是。有人能指点我一个关于互斥和条件的教程(最好是POSIX,而不是Windows),所以我可以尝试解决所有这些问题吗?
答案 0 :(得分:1)
如果我在内存中有一段数据(例如,指向数据的指针) 结构),一个线程可以随意写入它 另一个随意阅读
答案是“它取决于”通常意味着“不”。
根据您正在编写/阅读的内容,并且根据您的程序的逻辑,如果您尝试在没有同步的情况下进行写入和阅读,并且您不能完全确定写入和写入,那么您可能会遇到疯狂的结果或损坏。读取是原子的。
所以你应该使用互斥锁,除非:
Interlocked
系列等原子操作使用某种特定支持来自WinAPI的功能。)另外值得注意的是,你的while (using);
构造会更可靠,更正确,如果使用spin lock,甚至可能表现更好(如果你绝对确定需要旋转的话)锁定,而不是互斥锁。)
答案 1 :(得分:1)
您需要的工具称为原子操作,这将确保读取器线程只读取由另一个线程写入的整个数据。如果您不使用此类操作,则数据可能只能部分读取,因此根据您的应用程序读取的数据可能完全没有意义。
新标准C11具有这些操作,但尚未广泛实施。但是许多编译器应该具有实现这些的扩展。例如,gcc有一系列内置函数,以__sync
前缀开头。
答案 2 :(得分:0)
'google'中有很多手册页。搜索他们。我在几分钟内找到了http://www.yolinux.com/TUTORIALS/LinuxTutorialPosixThreads.html:
此外,从一个很小的例子开始,增加难度。 Firstable与线程创建和终止,线程返回,线程sincronization。继续使用posix互斥和条件并理解所有这些术语。
一个重要的文档提要是linux man和info pages。
祝你好运答案 3 :(得分:0)
如果我在内存中有一段数据(例如,指向数据结构的指针),一个线程是否可以随意写入,而另一个线程可以随意读取,或者每个线程都必须使用互斥锁来锁定内存,然后读或写,然后解锁?
如果在内存中有两个不同线程正在读写的数据部分,这称为临界区,这是消费者和生产者的常见问题。
有很多资源可以解决这个问题:
https://docs.oracle.com/cd/E19455-01/806-5257/sync-31/index.html
https://stackoverflow.com/questions/tagged/producer-consumer
但是,如果你打算使用两个不同的线程进行读写,则必须实现互斥锁的使用或其他形式的锁定和解锁。