用于 C++ 对象销毁的 C# 一次性模式
C# disposable pattern for C++ object destruction
我遇到了微软提供的这种一次性模式的实现:https://msdn.microsoft.com/en-us/library/system.idisposable(v=vs.110).aspx
using System;
class BaseClass : IDisposable
{
// Flag: Has Dispose already been called?
bool disposed = false;
// Public implementation of Dispose pattern callable by consumers.
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
// Protected implementation of Dispose pattern.
protected virtual void Dispose(bool disposing)
{
if (disposed)
return;
if (disposing) {
// Free any other managed objects here.
//
}
// Free any unmanaged objects here.
//
disposed = true;
}
~BaseClass()
{
Dispose(false);
}
}
假设我有一个与此 C# class 关联的 C++ class,并且我想 delete
C++ 对象处理 C# class 以确保我的非托管资源已正确释放。我添加了一个函数 DestructNative(self)
,它基本上对关联的 C++ 对象进行本机 C++ 调用 delete (CppObject*)self
。所以我的代码看起来像这样:
// Protected implementation of Dispose pattern.
protected virtual void Dispose(bool disposing)
{
if (disposed)
return;
if (disposing) {
// Free any other managed objects here.
//
}
DestructNative(self);
disposed = true;
}
所以我的问题是,知道可以从不同的线程调用 C# 终结器,我是否需要在 C++ 对象的析构函数中提供同步以确保在 Dispose(false)
调用时我没有任何竞争条件来自 C# 终结器?
附加问题
微软一次性模式坏了吗?似乎 disposed
标志是一个简单的变量,如果从不同的线程调用终结器,则该变量不会同步。
Is microsoft disposable pattern broken? Seems like disposed flag is a simple variable which is not synchronized if finalizer is called from a different thread.
不,它没有坏。
这个问题提出了2个有趣的问题。对于早于 C++11
思考并且线程不知道的 class,以下的影响是什么。
class PreCpp11 {
public:
int ** ptr;
bool mInitDone;
PreCpp11() : mInitDone(false) {
ptr = new int*[100];
}
init() {
for( size_t i = 0; i < 100; i++ ){
ptr[i] = new int[100];
}
mInitDone = true;
}
~PreCpp11() {
if( mInitDone ){
for( size_t i =0; i <100; i++ ){
delete ptr[i];
}
}
delete []ptr;
}
}
代码后
PreCpp11 * myObj = new PreCpp11();
myObj->init();
send_object_to_thread2( myObj );
线程 2 执行的位置
PreCpp11 obj = get_obj_from_sync();
delete obj;
如果在不同的线程上调用析构函数,我们如何避免数据竞争。
鉴于一次性实施,是否会导致上述数据竞争。
在这两种情况下,我认为此代码的答案是可以接受且合规的。但是,它依赖于 PreCpp11 对象的线程间通信以使其自身兼容。
我的想法....
我有一大堆数据竞争机会,这个线程保证看到我写入ptr数组的值,但其他线程不保证发生了inter-thread happens-before
关系。
但是,当我与第二个线程进行线程间通信时 class,然后发生同步以确保我的指针在启动线程和 "disposing" 线程之间正确同步,创建一个 inter-thread happens-before
关系,鉴于此发生在我调用 init
之后,线程 2 中看到的值是线程 1 看到的值,当它开始与第二个线程通信时。
因此,如果线程 1 在将对象提供给线程 2 后继续修改对象,则可能会发生数据竞争,但假设线程之间的通信是兼容的,那么第二个线程会看到第一个行为。
前序
->init() is sequenced before
send_object_to_thread2( myObj );
发生在
之前
->init() happens before the synchronized communication with thread2.
线程间happens-before
->init() happens before thread 2 gets the data and calls the destructor
->init() is sequenced-before the synchronized write to the inter-thread communication, and the write occurs before the synchronized read.
The actual write-read is ordered, as they are synchronized.
因此只要对象的线程间通信是同步的,并且不会发生对象的进一步修改post- 切换到新线程,就不会发生数据竞争。
我遇到了微软提供的这种一次性模式的实现:https://msdn.microsoft.com/en-us/library/system.idisposable(v=vs.110).aspx
using System;
class BaseClass : IDisposable
{
// Flag: Has Dispose already been called?
bool disposed = false;
// Public implementation of Dispose pattern callable by consumers.
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
// Protected implementation of Dispose pattern.
protected virtual void Dispose(bool disposing)
{
if (disposed)
return;
if (disposing) {
// Free any other managed objects here.
//
}
// Free any unmanaged objects here.
//
disposed = true;
}
~BaseClass()
{
Dispose(false);
}
}
假设我有一个与此 C# class 关联的 C++ class,并且我想 delete
C++ 对象处理 C# class 以确保我的非托管资源已正确释放。我添加了一个函数 DestructNative(self)
,它基本上对关联的 C++ 对象进行本机 C++ 调用 delete (CppObject*)self
。所以我的代码看起来像这样:
// Protected implementation of Dispose pattern.
protected virtual void Dispose(bool disposing)
{
if (disposed)
return;
if (disposing) {
// Free any other managed objects here.
//
}
DestructNative(self);
disposed = true;
}
所以我的问题是,知道可以从不同的线程调用 C# 终结器,我是否需要在 C++ 对象的析构函数中提供同步以确保在 Dispose(false)
调用时我没有任何竞争条件来自 C# 终结器?
附加问题
微软一次性模式坏了吗?似乎 disposed
标志是一个简单的变量,如果从不同的线程调用终结器,则该变量不会同步。
Is microsoft disposable pattern broken? Seems like disposed flag is a simple variable which is not synchronized if finalizer is called from a different thread.
不,它没有坏。
这个问题提出了2个有趣的问题。对于早于 C++11
思考并且线程不知道的 class,以下的影响是什么。
class PreCpp11 {
public:
int ** ptr;
bool mInitDone;
PreCpp11() : mInitDone(false) {
ptr = new int*[100];
}
init() {
for( size_t i = 0; i < 100; i++ ){
ptr[i] = new int[100];
}
mInitDone = true;
}
~PreCpp11() {
if( mInitDone ){
for( size_t i =0; i <100; i++ ){
delete ptr[i];
}
}
delete []ptr;
}
}
代码后
PreCpp11 * myObj = new PreCpp11();
myObj->init();
send_object_to_thread2( myObj );
线程 2 执行的位置
PreCpp11 obj = get_obj_from_sync();
delete obj;
如果在不同的线程上调用析构函数,我们如何避免数据竞争。
鉴于一次性实施,是否会导致上述数据竞争。
在这两种情况下,我认为此代码的答案是可以接受且合规的。但是,它依赖于 PreCpp11 对象的线程间通信以使其自身兼容。
我的想法....
我有一大堆数据竞争机会,这个线程保证看到我写入ptr数组的值,但其他线程不保证发生了inter-thread happens-before
关系。
但是,当我与第二个线程进行线程间通信时 class,然后发生同步以确保我的指针在启动线程和 "disposing" 线程之间正确同步,创建一个 inter-thread happens-before
关系,鉴于此发生在我调用 init
之后,线程 2 中看到的值是线程 1 看到的值,当它开始与第二个线程通信时。
因此,如果线程 1 在将对象提供给线程 2 后继续修改对象,则可能会发生数据竞争,但假设线程之间的通信是兼容的,那么第二个线程会看到第一个行为。
前序
->init() is sequenced before
send_object_to_thread2( myObj );
发生在
之前->init() happens before the synchronized communication with thread2.
线程间happens-before
->init() happens before thread 2 gets the data and calls the destructor
->init() is sequenced-before the synchronized write to the inter-thread communication, and the write occurs before the synchronized read.
The actual write-read is ordered, as they are synchronized.
因此只要对象的线程间通信是同步的,并且不会发生对象的进一步修改post- 切换到新线程,就不会发生数据竞争。