我有一个无法真正改变的程序机制:
我有一个struct
用于上下文。
我使用上下文的指针,而不是struct
对象本身,因为我必须将此上下文传递给非托管代码,并且非托管代码将仅接受指针作为参数 ,以便稍后在回调时还给我。
我需要对上下文的特定功能使用任何限制性方法,以便只能同时调用一次。
这样,我将使用SemaphoreSlim
,或者如果我不能使用它,那么我仍将使用ManualResetEvent
或AutoResetEvent
作为解决方法,从技术上阻止了上下文,直到非托管代码调用回调为止,这样我就可以释放锁。
因为我需要传递一个指向非托管代码的指针,所以我要使用struct
来将上下文Context*
转换为GCHandle.Alloc()
,方法是使用GCHandle.AddrOfPinnedObject()
固定由struct
。
问题出在我的上下文中SemaphoreSlim
,Manual/AutoResetEvent
或SemaphoreSlim ss1 = new SemaphoreSlim(0);
SemaphoreSlim* pointerOfSemaphore = &ss1;
SemaphoreSlim ss2 = *pointerOfSemaphore;
ss2.Release();
是托管类型,这使我无法获取它们,或者无法获得上下文的指针。
是否有任何解决方案来固定这些托管对象并获取它们的指针,然后将它们投射回对象?
例如:
Manual/AutoResetEvent
我知道,因为这不是值类型,所以无法通过这种方式获取其地址,但是还有其他解决方法吗?
或者,作为一种替代解决方案,我可以使用IntPtr
并以WaitOne()
的形式获取它们的句柄,但是我不知道如何释放其Set()
事件(向{{1}发出信号})只能使用Handle
。
任何想法都会受到赞赏!
答案 0 :(得分:0)
好吧,我发现这确实可行,但是我想知道是否有更好的解决方案:
unsafe
{
AutoResetEvent myEvent = new AutoResetEvent(false);
AutoResetEvent coEvent = new AutoResetEvent(false);
void* eventPtr = (void*)myEvent.Handle;
Console.WriteLine("Before release");
ThreadPool.QueueUserWorkItem(delegate
{
coEvent.Handle = (IntPtr)eventPtr;
Thread.Sleep(3000);
coEvent.Set();
}, null);
Console.WriteLine("Waiting for release");
myEvent.WaitOne();
Console.WriteLine("Event released");
}
(我知道我不必将IntPtr
转换为void*
再转换回IntPtr
,我是故意这样做的,因为我以这种方式通过了它移至非托管代码。)
通过一些“未公开说明”的代码,并在here的帮助下,我对SemaphoreSlim
进行了变通,并且确实可行。
unsafe
{
// Create semaphore
SemaphoreSlim mySemaphore = new SemaphoreSlim(0);
// Get size of semaphore
SemaphoreSlim[] mySemArr = new SemaphoreSlim[2];
TypedReference mySemRef1 = __makeref(mySemArr[0]);
TypedReference mySemRef2 = __makeref(mySemArr[1]);
int mySemaphoreSize = (int)((UInt64)(*((IntPtr*)&mySemRef2)) - (UInt64)(*((IntPtr*)&mySemRef1)));
// Get pointer of semaphore
TypedReference mySemaphoreTypeRef = __makeref(mySemaphore);
IntPtr mySemaphorePtr = *(IntPtr*)(&mySemaphoreTypeRef);
Console.WriteLine("Before release");
// Start new thread
ThreadPool.QueueUserWorkItem(delegate
{
// Convert IntPtr to byte "array"
byte* sourcePtr = (byte*)mySemaphorePtr;
// Create dummy semaphore
SemaphoreSlim castedSemaphore = default(SemaphoreSlim);
TypedReference castedSemaphoreTypeRef = __makeref(castedSemaphore);
byte* castedSemaphorePtr = (byte*)*((IntPtr*)&castedSemaphoreTypeRef);
// Copy mySemaphore to dummy
for (int i = 0; i < mySemaphoreSize; ++i)
castedSemaphorePtr[i] = sourcePtr[i];
// Wait 3 seconds
Thread.Sleep(3000);
// Release semaphore
castedSemaphore.Release();
}, null);
// Wait for release
Console.WriteLine("Waiting for release");
mySemaphore.Wait();
// Released
Console.WriteLine("Semaphore released");
}
简化版:
unsafe
{
// Create semaphore
SemaphoreSlim mySemaphore = new SemaphoreSlim(0);
// Get pointer of semaphore
TypedReference mySemaphoreTypeRef = __makeref(mySemaphore);
byte* mySemaphorePtr = (byte*)*(IntPtr*)(&mySemaphoreTypeRef);
Console.WriteLine("Before release");
// Start new thread
ThreadPool.QueueUserWorkItem(delegate
{
// Create dummy semaphore
SemaphoreSlim castedSemaphore = default(SemaphoreSlim);
TypedReference castedSemaphoreTypeRef = __makeref(castedSemaphore);
byte* castedSemaphorePtr = (byte*)(*((IntPtr*)&castedSemaphoreTypeRef));
// Copy mySemaphore to dummy
for (int i = 0; i < sizeof(IntPtr); ++i)
castedSemaphorePtr[i] = mySemaphorePtr[i];
// Wait 3 seconds
Thread.Sleep(3000);
// Release semaphore
castedSemaphore.Release();
}, null);
// Wait for release
Console.WriteLine("Waiting for release");
mySemaphore.Wait();
// Released
Console.WriteLine("Semaphore released");
}
即使它正在工作,但我对这种解决方案并不满意。还有更好的主意吗?