多线程和互斥锁

3

我目前正在使用跨平台库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),这样我就可以尝试弄清楚所有这些了吗?

一些 Allegro 的注意事项:Allegro 已经在需要的特定平台上以不同的线程运行这些各种组件。还要注意,给定显示器的所有绘图操作都需要从创建该显示器的同一线程中进行。如果您从辅助线程加载位图,则可以在显示线程上调用 al_clone_bitmap()... 但是您不应该在每个帧上都这样做。在我看来,您不应该寻找使用更多线程的原因。 - Matthew
4个回答

1
如果我在内存中有一段数据(比如一个指向数据结构的指针),一个线程随意写入,另一个线程随意读取是否可以?
答案是“要看情况”,通常意味着“不行”。
根据你正在写入/读取的内容以及程序逻辑,如果没有同步并且确定写入和读取是原子的,你可能会得到错误的结果或损坏。
因此,除非:
1. 你绝对确定写入和读取是原子的,并且你绝对确定一个线程仅用于读取(最好使用某种特定的原子操作支持,如WinAPI的Interlocked函数族)。 2. 你绝对需要从不锁定中获得微小的性能增益。
你应该使用一个互斥锁。
还值得注意的是,如果您使用自旋锁(而不是互斥锁),则您的while (using);构造将更加可靠、正确,并且可能甚至执行更快。

1
你需要的工具叫做原子操作,它可以确保读取线程只读取其他线程写入的完整数据。如果不使用这样的操作,数据可能只被部分读取,因此读取到的内容可能与应用程序无关。

新标准C11包含了这些操作,但尚未广泛实现。但是许多编译器应该有实现这些操作的扩展。例如,gcc有一系列以__sync为前缀的内置函数。


0

'Google'里有很多手册,可以搜索一下。我在几分钟内找到了http://www.yolinux.com/TUTORIALS/LinuxTutorialPosixThreads.html

此外,从一个简单的例子开始,逐渐增加难度。首先是线程的创建和终止、线程返回值、线程同步。然后学习posix互斥锁和条件变量,并理解所有这些术语。

Linux man和info页面是重要的文档来源。

祝你好运!


0
如果我在内存中有一段数据(比如一个指向数据结构的指针),一个线程随意写入,另一个线程随意读取,这样可以吗?还是每个线程都必须使用互斥锁来锁定内存,然后读取或写入,然后解锁?
如果你在内存中有一段数据,两个不同的线程正在读写,这被称为临界区,是生产者和消费者的常见问题。
有许多资源可以解决这个问题:

如果你要使用两个不同的线程来读写,那么你必须实现互斥锁或其他形式的锁定和解锁。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接