假设我想使用.Net Framework
中的非线程安全类(文档声明它不是线程安全的)。有时我从一个线程更改Property X
的值,有时从另一个线程更改,但我从不同时从两个线程访问它。有时我从一个线程调用Method Y
,有时从另一个线程调用,但从不同时调用。{/ p>
这是否意味着我以线程安全的方式使用该类,以及文档声明它不是线程安全的这一事实 与我的情况不再相关?
如果答案是否:我是否可以在同一个线程中执行与特定对象相关的所有操作 - 即创建它并始终在同一个线程(但不是GUI线程)中调用其成员?如果是这样,我该怎么做? (如果相关,它是一个WPF应用程序。)
答案 0 :(得分:3)
不,它不是线程安全的。作为一般规则,如果没有某种同步,就不应该编写多线程代码。在您的第一个示例中,即使您以某种方式设法确保修改/读取永远不会同时进行,仍然存在缓存值和指令重新排序的问题。
例如,CPU将值缓存到寄存器中,您在一个线程上更新它,从另一个线程读取它。如果第二个缓存了它,它就不会进入RAM来获取它并且没有看到更新的值。
有关编写无锁多线程代码link的更多信息和问题,请查看这篇精彩文章。它有一个很好的解释,CPU,编译器和CLI字节码编译器如何重新排序指令。
答案 1 :(得分:2)
假设我想使用.Net Framework中的非线程安全类(文档声明它不是线程安全的)。
“线程安全”有许多不同的含义。大多数对象分为三类:
有时我从一个线程更改属性X的值,有时从另一个线程更改,但我从不同时从两个线程访问它。有时我从一个线程调用Method Y,有时从另一个线程调用,但从不同时调用。
正如另一位回答者所指出的,你必须考虑指令重新排序和缓存读取。换句话说,仅仅在不同时间执行这些操作是不够的;你需要实施适当的障碍,以确保它能够正常工作。
最简单的方法是使用lock
语句保护对象的所有访问权限。如果所有读取,写入和方法调用都在同一个锁中,那么这将起作用(假设对象确实具有一次一种的线程模型而不是线程仿射)。
答案 2 :(得分:1)
假设我想使用.Net Framework中的非线程安全类(文档声明它不是线程安全的)。有时我从一个线程更改属性X的值,有时从另一个线程更改,但我从不同时从两个线程访问它。有时我从一个线程调用Method Y,有时从另一个线程调用,但从不同时调用。
默认情况下,所有Classes
都是非线程安全的,除了Collections
之类的Concurrent Collections
专门为thread safety
设计的multiple threads
。因此,对于您可以选择的任何其他课程,如果您通过Non atomic
或read / write
方式访问它,无论Async-Await
是否必须在更改时引入线程安全性对象的状态。这仅适用于可以在多线程环境中修改其状态的对象,但这样的方法只是功能实现,它们本身不是可以修改的状态,它们只是引入线程安全来维护对象状态。 / p>
这是否意味着我以线程安全的方式使用该类,并且文档声明它不是线程安全的事实不再与我的情况相关?如果答案是否:我可以在同一个线程(但不是GUI线程)中执行与类相关的所有操作吗?如果是这样,我该怎么做? (如果相关,它是一个WPF应用程序。)
对于Ui应用程序,请考虑为基于IO的操作引入TPL
,例如文件读取,数据库读取和使用Async-Await
进行计算绑定操作。 await
的好处是:
Async
Ui控件可以直接更新而没有Cross线程关注,因为只涉及一个线程最后:有一个类,其中一个方法启动一个操作,另一个方法结束它。例如,使用SpeechRecognitionEngine类,您可以使用RecognizeAsync启动语音识别会话(此方法在TPL库之前,因此它不返回任务),然后使用RecognizeAsyncCancel取消识别会话。如果我从一个线程调用RecognizeAsync并从另一个线程调用RecognizeAsyncCancel怎么办? (它有效,但它是否安全"?它会在某些我不知道的情况下失败吗?)
正如您提到的APM
方法,这可能是一个较早的实现,基于AsyncCallBack
,需要BeginXX, EndXX
来协调,Async-Await
行如果是这种情况,那么协调就不需要太多,因为他们使用AsyncCallBack来执行回调委托。实际上如前所述,这里没有涉及额外的线程,无论是旧版本还是新版本CancellationTokenSource
。关于任务取消,Async-Await
可以用于await Task.Run(() => RecognizeAsync())
,不需要单独的取消任务。在多个线程之间,可以通过Auto / Manual ResetEvent进行协调。
如果上面提到的调用是同步的,那么使用Task包装器返回Task可以通过Async方法调用它们,如下所示:
Async
虽然它是一种反模式,但在制作整个调用链时很有用lock, mutex, semaphore, monitor, Interlocked,
编辑(回答OP问题)
感谢您的详细解答,但我对其中的一些内容并不了解。在第一点,你说"引入线程安全"是必要的,但是如何?
IO completion ports
等同步构造引入线程安全,所有这些都用于保存对象免于损坏/竞争条件。我没有看到任何步骤。我的帖子中描述的步骤是否足够?
在第二点,我一直在询问如何在同一个线程中使用对象(每当我使用它时)。 Async-Await与此无关,AFAIK。
Task Parallel library
(基于硬件的并发) ),否则如果您使用{{1}},那么您无法确保始终使用相同/给定的线程,因为这是一个非常高级别的抽象查看我最近关于线程here的详细答案之一,它可能有助于提供更详细的方面
答案 3 :(得分:0)
由于技术风险存在,它不是线程安全的,但您的策略旨在解决问题并解决风险。因此,如果事情符合您的描述,那么您就没有线程安全的环境,但是,您是安全的。现在。