作为一名开发人员,他刚刚在项目中编写了数千行复杂的多线程“C”代码,并且将来会被其他几位不熟悉此代码的开发人员进行增强,修改等。我想知道你们试图在这些代码中放入什么样的安全网?作为一个例子,我可以做到这些:
您在已编写的多线程代码中添加了哪些安全网?
当其他开发人员修改此类代码时,您遇到了哪些问题?
你在这些代码中加入了哪些调试工具?
感谢您的评论。
答案 0 :(得分:6)
我们在产品中做了很多事情(一个旨在帮助您找到应用程序中的并发错误的虚拟机管理程序),它们通常更有用。请注意,我们在代码本身中执行这些操作(因为它是一个高度并发的软件),无论您是否编写并发代码,其中一些都很有用。
和你一样,我们有能力断言(lock_held(...))并使用它。
我们也(因为我们有自己的调度程序)可以为那些(罕见的)情况断言(single_threaded()),我们指望系统中没有其他线程处于活动状态。
从一个线程到另一个线程的内存损坏很常见(并且很难调试)所以我们做了两件事来解决这个问题:在我们的线程堆栈中撒了一些神奇的cookie。我们定期(在我们的get_thread_id())函数中调用“validate_thread_stack()”函数来检查这些cookie,以确保堆栈没有损坏。
我们的malloc在malloc内存块之前和之后粘贴魔术饼干并免费检查。如果有人超出他们的数据,这些可以用来尽早发现腐败。
在free()上,我们在内存中创建了一个众所周知的模式(在我们的例子中是0xdddd ...)。这很好地腐蚀了其他任何有悬挂指针留在那个记忆区域的人。
我们在线程堆栈底部附近有一个保护页面(未映射到地址空间的内存页面)。如果线程超出其堆栈,我们通过页面错误捕获它并放入我们的调试器。
见证了我们的锁。查看FreeBSD锁定见证代码。它就像那样但是自制软件。基本上,见证代码是一种通过查看锁定采集图中的周期来检测潜在死锁的轻量级方法。
我们的锁也包含了记录获取和释放的文件/行号的访问器。对于双重解锁或双重锁定,您可以获得关于螺旋上升的漂亮调试信息。
我们的锁也被分析。一旦你的代码工作,你希望它运作良好。我们跟踪通常的事情,例如收购多少次,获得它需要多长时间。
在我们的系统中,我们期望锁不会争用(我们以这种方式仔细设计了代码)。因此,如果您在我们的系统中等待超过一秒或两秒的旋转锁定,您将被放入调试器中,因为它很可能不是一件好事。
我们要原子更新的变量包含在C struct的内部。这样做的原因是为了防止混淆代码的好用:atomic_increment(& var);并且使用var ++。我们很难编写后一段代码。
我们的代码库中禁止使用“volatile”,因为编译器模糊地实现了它。尝试拼凑同步是一种糟糕的方式。
当然还有代码审查。如果你无法向同事解释你的并发假设和锁定纪律,那么代码肯定存在问题: - )
答案 1 :(得分:2)
让一切变得非常明显,以便其他开发人员在单独查看代码的子部分时不会错过同步范围。
例如:不要在跨越多个文件的代码中保持锁定。
答案 2 :(得分:1)
好像你已经回答了自己的问题:在代码中添加了很多断言。他们会告诉其他开发人员必须持有不变量和前置条件。