实现线程安全 [英] Achieving Thread-Safety

查看:85
本文介绍了实现线程安全的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

问题:如何确保我的应用程序是线程安全的?是他们的任何常规做法,测试方法,要避免的事情,要寻找的事情吗?

Question How can I make sure my application is thread-safe? Are their any common practices, testing methods, things to avoid, things to look for?

背景我当前正在开发一个服务器应用程序,该应用程序在不同的线程中执行许多后台任务,并使用Indy(使用另一组自动生成的线程进行通信)与客户端进行通信.由于该应用程序应该具有很高的可用性,因此程序崩溃是一件非常糟糕的事情,我想确保该应用程序是线程安全的.无论如何,有时我会发现一段代码抛出一个从未发生过的异常,在大多数情况下,我意识到这是某种同步错误,我忘记了正确同步对象.因此,我的问题涉及最佳实践,线程安全性测试以及类似的事情.

Background I'm currently developing a server application that performs a number of background tasks in different threads and communicates with clients using Indy (using another bunch of automatically generated threads for the communication). Since the application should be highly availabe, a program crash is a very bad thing and I want to make sure that the application is thread-safe. No matter what, from time to time I discover a piece of code that throws an exception that never occured before and in most cases I realize that it is some kind of synchronization bug, where I forgot to synchronize my objects properly. Hence my question concerning best practices, testing of thread-safety and things like that.

mghie:感谢您的回答!我也许应该更精确一些.明确地说,我了解多线程的原理,在整个程序中使用同步(监视器),并且知道如何将线程问题与其他实现问题区分开.但是,尽管如此,我仍然忘记不时添加适当的同步.仅举一个例子,我在代码中使用了RTL排序功能.看起来像

mghie: Thanks for the answer! I should perhaps be a little bit more precise. Just to be clear, I know about the principles of multithreading, I use synchronization (monitors) throughout my program and I know how to differentiate threading problems from other implementation problems. But nevertheless, I keep forgetting to add proper synchronization from time to time. Just to give an example, I used the RTL sort function in my code. Looked something like

FKeyList.Sort (CompareKeysFunc);

结果是,我必须在排序时同步FKeyList.最初编写那行简单的代码时,我只是没有想到.我想谈的就是这些.在哪些地方容易忘记添加同步代码?如何确保在所有重要位置添加了同步代码?

Turns out, that I had to synchronize FKeyList while sorting. It just don't came to my mind when initially writing that simple line of code. It's these thins I wanna talk about. What are the places where one easily forgets to add synchronization code? How do YOU make sure that you added sync code in all important places?

推荐答案

您不能真正测试线程安全性.您所能做的就是证明您的代码不是线程安全的,但是如果您知道该怎么做,那么您已经知道该如何在程序中修复该特定的错误.问题是您不知道的错误,您将如何编写这些错误的测试?除此之外,线程问题比其他问题更难发现,因为调试行为已经可以改变程序的行为.从一个程序运行到下一个程序,从一台机器运行到另一台机器,事情将有所不同. CPU和CPU内核的数量,并行运行的程序的数量和种类,程序中发生的事情的确切顺序和时间-所有这些以及更多这些都会影响程序的行为. [我实际上想将月相和类似的东西添加到此列表中,但是您明白了我的意思.]

You can't really test for thread-safeness. All you can do is show that your code isn't thread-safe, but if you know how to do that you already know what to do in your program to fix that particular bug. It's the bugs you don't know that are the problem, and how would you write tests for those? Apart from that threading problems are much harder to find than other problems, as the act of debugging can already alter the behaviour of the program. Things will differ from one program run to the next, from one machine to the other. Number of CPUs and CPU cores, number and kind of programs running in parallel, exact order and timing of stuff happening in the program - all of this and much more will have influence on the program behaviour. [I actually wanted to add the phase of the moon and stuff like that to this list, but you get my meaning.]

我的建议是停止将其视为实现问题,而开始将其视为程序设计问题.无论是否为Delphi编写,都需要学习并阅读所有有关多线程的知识.最后,您需要了解基本原理,并在编程中正确应用它们.操作系统提供了诸如关键部分,互斥体,条件和线程之类的基元,并且大多数语言仅将它们包装在它们的库中(这忽略了诸如Erlang等提供的绿色线程之类的东西,但这是一个很好的出发点).

My advice is to stop seeing this as an implementation problem, and start to look at this as a program design problem. You need to learn and read all that you can find about multi-threading, whether it is written for Delphi or not. In the end you need to understand the underlying principles and apply them properly in your programming. Primitives like critical sections, mutexes, conditions and threads are something the OS provides, and most languages only wrap them in their libraries (this ignores things like green threads as provided by for example Erlang, but it's a good point of view to start out from).

我想从关于线程的Wikipedia文章开始,然后按您的方式工作通过链接的文章.我从Aaron Cohen和Mike Woodring的"Win32 Multithreaded Programming" 开始绝版,但也许您可以找到类似的东西.

I'd say start with the Wikipedia article on threads and work your way through the linked articles. I have started with the book "Win32 Multithreaded Programming" by Aaron Cohen and Mike Woodring - it is out of print, but maybe you can find something similar.

修改:让我简要介绍您修改后的问题.所有对非只读数据的访问都必须正确同步以确保线程安全,并且对列表进行排序不是只读操作.因此显然,需要在所有对列表的访问周围添加同步.

Let me briefly follow up on your edited question. All access to data that is not read-only needs to be properly synchronized to be thread-safe, and sorting a list is not a read-only operation. So obviously one would need to add synchronization around all accesses to the list.

但是随着系统中内核越来越多,常量锁定将限制可以完成的工作量,因此,寻找一种不同的方法来设计程序是一个好主意.一种想法是在程序中引入尽可能多的只读数据-不再需要锁定,因为所有访问都是只读的.

But with more and more cores in a system constant locking will limit the amount of work that can be done, so it is a good idea to look for a different way to design your program. One idea is to introduce as much read-only data as possible into your program - locking is no longer necessary, as all access is read-only.

我发现接口对于设计多线程程序非常有用.可以将接口实现为仅具有对内部数据进行只读访问的方法,并且如果您坚持使用这些接口,则可以确定不会发生很多潜在的编程错误.您可以在线程之间自由共享它们,并且线程安全的引用计数将确保在最后一个对它们的引用超出范围或分配了另一个值时,可以正确释放实现对象.

I have found interfaces to be a very valuable aid in designing multi-threaded programs. Interfaces can be implemented to have only methods for read-only access to the internal data, and if you stick to them you can be quite sure that a lot of the potential programming errors do not occur. You can freely share them between threads, and the thread-safe reference counting will make sure that the implementing objects are properly freed when the last reference to them goes out of scope or is assigned another value.

您要做的是创建从TInterfacedObject继承的对象.它们实现一个或多个接口,这些接口全部仅提供对对象内部的只读访问,但是它们也可以提供使对象状态发生变化的公共方法.创建对象时,请同时保留对象类型的变量和接口指针变量.这样,生命周期管理很容易,因为在发生异常时将自动删除对象.您使用指向该对象的变量来调用正确设置该对象所需的所有方法.这会改变内部状态,但是由于这种情况仅发生在活动线程中,因此没有潜在的冲突.一旦正确设置了对象,就将接口指针返回到调用代码,并且由于此后除了访问接口指针之外,没有其他方法可以访问该对象,因此可以确保只能执行只读访问.通过使用这种技术,您可以完全消除对象内部的锁定.

What you do is create objects that descend from TInterfacedObject. They implement one or more interfaces which all provide only read-only access to the internals of the object, but they can also provide public methods that mutate the object state. When you create the object you keep both a variable of the object type and a interface pointer variable. That way lifetime management is easy, because the object will be deleted automatically when an exception occurs. You use the variable pointing to the object to call all methods necessary to properly set up the object. This mutates the internal state, but since this happens only in the active thread there is no potential for conflict. Once the object is properly set up you return the interface pointer to the calling code, and since there is no way to access the object afterwards except by going through the interface pointer you can be sure that only read-only access can be performed. By using this technique you can completely remove the locking inside of the object.

如果需要更改对象的状态怎么办?不需要,您可以通过从接口复制数据来创建一个新对象,然后再更改新对象的内部状态.最后,将引用指针返回到新对象.

What if you need to change the state of the object? You don't, you create a new one by copying the data from the interface, and mutate the internal state of the new objects afterwards. Finally you return the reference pointer to the new object.

通过使用此方法,您仅需要在获取或设置此类接口的位置进行锁定.通过使用原子交换功能,甚至可以在不锁定的情况下完成此操作.请参阅Primoz Gabrijelcic的此博客帖子.设置接口指针的类似用例.

By using this you will only need locking where you get or set such interfaces. It can even be done without locking, by using the atomic interchange functions. See this blog post by Primoz Gabrijelcic for a similar use case where an interface pointer is set.

这篇关于实现线程安全的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆