调试多线程程序

时间:2012-02-15 05:35:32

标签: c++ multithreading

如果有两个线程试图同时写入同一个对象的代码,我的理解是它不会产生任何编译时错误,这是调试多线程程序的原因之一难。但这会产生运行时错误/异常吗?

有人能建议任何好的多线程调试技术吗?

感谢。

5 个答案:

答案 0 :(得分:1)

从两个或多个不同的线程内部访问同一个对象不会在运行时或调试器中生成错误但是它很可能会以您不想要的方式使用该对象。

在多线程环境中安全地处理它的方法是使用互斥锁和信号量。 对于互斥锁,请检查wikipedia link

当您想要一次只限一个线程来限制对象的访问时,通常会使用互斥锁。

另一方面,信号量是一种更通用的情况(互斥体实际上是信号量的一种特殊情况),它具有一个计数器,每个线程将根据信号量的激活/去激活而增加/减少。当信号量达到0时,它将锁定自身和导致它的线程。有关信号量的更多信息,请查看wikipedia page

如果您需要更具体的建议,那么我建议您提供有关您所针对的操作系统和/或您正在使用的API的信息,因为与线程(互斥,信号量等)有关的任何操作都是特定于操作系统的< / p>

答案 1 :(得分:1)

同时写入单个对象或资源的两个不同线程会破坏它,这个问题就是竞争条件。在多线程程序中,竞争条件既不是编译时错误,也不是运行时错误/异常。竞争条件是管理共享资源(即进程间通信)的软件缺陷,并且因为它们破坏了幕后的数据而令人讨厌。多次运行相同的程序将导致输出是预期的结果,而在其他时候不是预期的结果。

通过使用互斥来防止线程中的竞争条件。如果只有一个对象或资源可用,则使用互斥锁,例如LCD显示器或单个对象,否则如果有多个使用信号量,则一个示例是四个USB端口。资源是数据和设备。数据是变量,对象,数据结构等。设备是LCD显示器,打印机,USB端口等。

将程序视为顺序单线程并确定需要完成的分离任务可以省去更多时间调试的麻烦。字处理程序是一个由多个线程组成的多线程程序。线程的虚构示例是:读取文本文件,显示文本,保存文本文件,以及自动保存每五分钟运行一次。线程应该是可以执行的操作,并注意文字处理器中的所有线程都将文本作为资源。

如果您已经有代码或检查值,请在每个线程中的对象之前和之后使用printf语句而不是cout。请参阅printf而不是cout here的原因。

所有操作系统都有进程间通信,但API不同。 Linux使用POSTIX API,Windows使用Win32或Windows API,但使用方法相同。

阅读材料 http://drdobbs.com/cpp/199200938?pgno=1

^ - 将一些文章概括为所写的内容

答案 2 :(得分:1)

如果您使用的是Linux或OS X,那么您可以使用其中一个valgrind工具(hellgrind或drd)来检测线程的一些内存访问,而无需相应的互斥锁。

然而,这不是万无一失的,我不会依赖它来解决你所有的问题。非常小心任何共享资源。

答案 3 :(得分:0)

可能会也可能不会。并发修改是大多数线程标准的UB,因此它们不保证会发生什么。通常,对于简单类型,一个或另一个写将“赢”。 (有些平台可以保证对齐,简单的类型。)对于复杂的类型,你可能会撕裂并结束“中间”值。

并发写入通常不是您遇到的问题类型。更常见的情况是重叠读 - 修改 - 写操作。例如,考虑两个线程试图将项添加到同一队列或链表。当您将项目添加到链接列表时,可能有一段时间链接列表被“破坏”,并且如果另一个线程在链接列表被破坏时访问链接列表(在修改过程中它的部分完成时),事情就可以了爆炸。

将项目添加到链接列表的头部通常包括:

object->next = head->next;
head = object;

如果另一个线程在第一行完成后但在第二行开始之前尝试将对象添加到链接列表,则结果将不同。

答案 4 :(得分:0)

似乎没有人回答如何实时调试多线程代码,这真的很难。极短的延迟会使程序的行为不同,只有在某些竞争条件发生时才会出现错误。这只能通过极快的跟踪来调查,因为Visual Studio太慢了。更糟糕的是,当2个线程同时调用它时,trace方法变得非常慢。

将自己的非阻塞(!)跟踪编写到内存中非常容易。只需将信息写入环形缓冲区即可。我的C ++不够好,但在C#中,代码看起来像这样:

const int maxMessages = 0x100;
const int indexMask = maxMessages-1;
string[] messages = new string[maxMessages];
int messagesIndex = -1;

public void Trace(string message) {
  int thisIndex = Interlocked.Increment(ref messagesIndex) & indexMask;
  messages[thisIndex] = message;
}

这种方法的更详细描述也收集了线程和时序信息并很好地输出了跟踪: CodeProject:实时调试多线程代码1