- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
我想知道在我下面的特定情况下,当您通过 P/Invoke 将回调委托(delegate)编码到 DLL 时,是否有可能成为本地世界中托管线程管理问题的受害者(参见示例代码)。
This MSDN article on Managed and Unmanaged Threading in Windows状态:
“操作系统 ThreadId 与托管线程没有固定关系,因为非托管主机可以控制托管线程和非托管线程之间的关系。具体来说,复杂的主机可以使用 Fiber API 来调度许多托管线程针对同一操作系统线程,或在不同操作系统线程之间移动托管线程。”
首先,本文描述的非托管主机 是谁或什么?如果您像我在下面给出的示例代码中那样使用编码(marshal)处理,那么那里的非托管主机是谁或什么?
此外,this StackOverflow question已接受的答案指出:
“从 CLR 的角度来看,单个托管线程在其生命周期内由多个不同的 native 线程支持是完全合法的。这意味着 GetCurrentThreadId 的结果可以(并且将会)在线程的整个生命周期内发生变化”
那么,这是否意味着我的 APC 将在 native 线程中排队,或者因为编码(marshal)处理层而直接委托(delegate)给我的托管线程?
这是示例。假设我使用以下类在托管代码中 P/调用 NotifyServiceStatusChange 函数以检查服务何时停止:
using System;
using System.Runtime.InteropServices;
namespace ServiceStatusChecking
{
class QueryServiceStatus
{
[System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential)]
public class SERVICE_NOTIFY
{
public uint dwVersion;
public IntPtr pfnNotifyCallback;
public IntPtr pContext;
public uint dwNotificationStatus;
public SERVICE_STATUS_PROCESS ServiceStatus;
public uint dwNotificationTriggered;
public IntPtr pszServiceNames;
};
[System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential)]
public struct SERVICE_STATUS_PROCESS
{
public uint dwServiceType;
public uint dwCurrentState;
public uint dwControlsAccepted;
public uint dwWin32ExitCode;
public uint dwServiceSpecificExitCode;
public uint dwCheckPoint;
public uint dwWaitHint;
public uint dwProcessId;
public uint dwServiceFlags;
};
[DllImport("advapi32.dll")]
static extern IntPtr OpenService(IntPtr hSCManager, string lpServiceName, uint dwDesiredAccess);
[DllImport("advapi32.dll")]
static extern IntPtr OpenSCManager(string machineName, string databaseName, uint dwAccess);
[DllImport("advapi32.dll")]
static extern uint NotifyServiceStatusChange(IntPtr hService, uint dwNotifyMask, IntPtr pNotifyBuffer);
[DllImportAttribute("kernel32.dll")]
static extern uint SleepEx(uint dwMilliseconds, bool bAlertable);
[DllImport("advapi32.dll")]
static extern bool CloseServiceHandle(IntPtr hSCObject);
delegate void StatusChangedCallbackDelegate(IntPtr parameter);
/// <summary>
/// Block until a service stops or is found to be already dead.
/// </summary>
/// <param name="serviceName">The name of the service you would like to wait for.</param>
public static void WaitForServiceToStop(string serviceName)
{
IntPtr hSCM = OpenSCManager(null, null, (uint)0xF003F);
if (hSCM != IntPtr.Zero)
{
IntPtr hService = OpenService(hSCM, serviceName, (uint)0xF003F);
if (hService != IntPtr.Zero)
{
StatusChangedCallbackDelegate changeDelegate = ReceivedStatusChangedEvent;
SERVICE_NOTIFY notify = new SERVICE_NOTIFY();
notify.dwVersion = 2;
notify.pfnNotifyCallback = Marshal.GetFunctionPointerForDelegate(changeDelegate);
notify.ServiceStatus = new SERVICE_STATUS_PROCESS();
GCHandle notifyHandle = GCHandle.Alloc(notify, GCHandleType.Pinned);
IntPtr pinnedNotifyStructure = notifyHandle.AddrOfPinnedObject();
NotifyServiceStatusChange(hService, (uint)0x00000001, pinnedNotifyStructure);
SleepEx(uint.MaxValue, true);
notifyHandle.Free();
CloseServiceHandle(hService);
}
CloseServiceHandle(hSCM);
}
}
public static void ReceivedStatusChangedEvent(IntPtr parameter)
{
}
}
}
APC 是在托管我的托管线程的任何 native 线程上排队,还是直接委托(delegate)给我的托管线程?我原以为代理是用来处理这种情况的,所以我们不需要担心托管线程是如何本地处理的,但我可能错了!
编辑:我想这是一个更令人满意的答案。
using System;
using System.Runtime.InteropServices;
using System.Threading;
namespace ServiceAssistant
{
class ServiceHelper
{
[System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential)]
public class SERVICE_NOTIFY
{
public uint dwVersion;
public IntPtr pfnNotifyCallback;
public IntPtr pContext;
public uint dwNotificationStatus;
public SERVICE_STATUS_PROCESS ServiceStatus;
public uint dwNotificationTriggered;
public IntPtr pszServiceNames;
};
[System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential)]
public struct SERVICE_STATUS_PROCESS
{
public uint dwServiceType;
public uint dwCurrentState;
public uint dwControlsAccepted;
public uint dwWin32ExitCode;
public uint dwServiceSpecificExitCode;
public uint dwCheckPoint;
public uint dwWaitHint;
public uint dwProcessId;
public uint dwServiceFlags;
};
[DllImport("advapi32.dll")]
static extern IntPtr OpenService(IntPtr hSCManager, string lpServiceName, uint dwDesiredAccess);
[DllImport("advapi32.dll")]
static extern IntPtr OpenSCManager(string machineName, string databaseName, uint dwAccess);
[DllImport("advapi32.dll")]
static extern uint NotifyServiceStatusChange(IntPtr hService, uint dwNotifyMask, IntPtr pNotifyBuffer);
[DllImportAttribute("kernel32.dll")]
static extern uint SleepEx(uint dwMilliseconds, bool bAlertable);
[DllImport("advapi32.dll")]
static extern bool CloseServiceHandle(IntPtr hSCObject);
delegate void StatusChangedCallbackDelegate(IntPtr parameter);
/// <summary>
/// Block until a service stops or is found to be already dead.
/// </summary>
/// <param name="serviceName">The name of the service you would like to wait for.</param>
/// <param name="timeout">An amount of time you would like to wait for. uint.MaxValue is the default, and it will force this thread to wait indefinitely.</param>
public static void WaitForServiceToStop(string serviceName, uint timeout = uint.MaxValue)
{
Thread.BeginThreadAffinity();
GCHandle? notifyHandle = null;
StatusChangedCallbackDelegate changeDelegate = ReceivedStatusChangedEvent;
IntPtr hSCM = OpenSCManager(null, null, (uint)0xF003F);
if (hSCM != IntPtr.Zero)
{
IntPtr hService = OpenService(hSCM, serviceName, (uint)0xF003F);
if (hService != IntPtr.Zero)
{
SERVICE_NOTIFY notify = new SERVICE_NOTIFY();
notify.dwVersion = 2;
notify.pfnNotifyCallback = Marshal.GetFunctionPointerForDelegate(changeDelegate);
notify.ServiceStatus = new SERVICE_STATUS_PROCESS();
notifyHandle = GCHandle.Alloc(notify, GCHandleType.Pinned);
IntPtr pinnedNotifyStructure = ((GCHandle)notifyHandle).AddrOfPinnedObject();
NotifyServiceStatusChange(hService, (uint)0x00000001, pinnedNotifyStructure);
SleepEx(timeout, true);
CloseServiceHandle(hService);
}
CloseServiceHandle(hSCM);
}
GC.KeepAlive(changeDelegate);
if (notifyHandle != null)
{
((GCHandle)notifyHandle).Free();
}
Thread.EndThreadAffinity();
}
public static void ReceivedStatusChangedEvent(IntPtr parameter)
{
}
}
}
再次编辑!我想这是一个更令人满意的答案:
using System;
using System.Runtime.InteropServices;
using System.Threading;
namespace ServiceAssistant
{
class ServiceHelper
{
[System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential)]
public class SERVICE_NOTIFY
{
public uint dwVersion;
public IntPtr pfnNotifyCallback;
public IntPtr pContext;
public uint dwNotificationStatus;
public SERVICE_STATUS_PROCESS ServiceStatus;
public uint dwNotificationTriggered;
public IntPtr pszServiceNames;
};
[System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential)]
public struct SERVICE_STATUS_PROCESS
{
public uint dwServiceType;
public uint dwCurrentState;
public uint dwControlsAccepted;
public uint dwWin32ExitCode;
public uint dwServiceSpecificExitCode;
public uint dwCheckPoint;
public uint dwWaitHint;
public uint dwProcessId;
public uint dwServiceFlags;
};
[DllImport("advapi32.dll")]
static extern IntPtr OpenService(IntPtr hSCManager, string lpServiceName, uint dwDesiredAccess);
[DllImport("advapi32.dll")]
static extern IntPtr OpenSCManager(string machineName, string databaseName, uint dwAccess);
[DllImport("advapi32.dll")]
static extern uint NotifyServiceStatusChange(IntPtr hService, uint dwNotifyMask, IntPtr pNotifyBuffer);
[DllImportAttribute("kernel32.dll")]
static extern uint SleepEx(uint dwMilliseconds, bool bAlertable);
[DllImport("advapi32.dll")]
static extern bool CloseServiceHandle(IntPtr hSCObject);
delegate void StatusChangedCallbackDelegate(IntPtr parameter);
/// <summary>
/// Block until a service stops, is killed, or is found to be already dead.
/// </summary>
/// <param name="serviceName">The name of the service you would like to wait for.</param>
/// <param name="timeout">An amount of time you would like to wait for. uint.MaxValue is the default, and it will force this thread to wait indefinitely.</param>
public static void WaitForServiceToStop(string serviceName, uint timeout = uint.MaxValue)
{
// Ensure that this thread's identity is mapped, 1-to-1, with a native OS thread.
Thread.BeginThreadAffinity();
GCHandle notifyHandle = default(GCHandle);
StatusChangedCallbackDelegate changeDelegate = ReceivedStatusChangedEvent;
IntPtr hSCM = IntPtr.Zero;
IntPtr hService = IntPtr.Zero;
try
{
hSCM = OpenSCManager(null, null, (uint)0xF003F);
if (hSCM != IntPtr.Zero)
{
hService = OpenService(hSCM, serviceName, (uint)0xF003F);
if (hService != IntPtr.Zero)
{
SERVICE_NOTIFY notify = new SERVICE_NOTIFY();
notify.dwVersion = 2;
notify.pfnNotifyCallback = Marshal.GetFunctionPointerForDelegate(changeDelegate);
notify.ServiceStatus = new SERVICE_STATUS_PROCESS();
notifyHandle = GCHandle.Alloc(notify, GCHandleType.Pinned);
IntPtr pinnedNotifyStructure = notifyHandle.AddrOfPinnedObject();
NotifyServiceStatusChange(hService, (uint)0x00000001, pinnedNotifyStructure);
SleepEx(timeout, true);
}
}
}
finally
{
// Clean up at the end of our operation, or if this thread is aborted.
if (hService != IntPtr.Zero)
{
CloseServiceHandle(hService);
}
if (hSCM != IntPtr.Zero)
{
CloseServiceHandle(hSCM);
}
GC.KeepAlive(changeDelegate);
if (notifyHandle != default(GCHandle))
{
notifyHandle.Free();
}
Thread.EndThreadAffinity();
}
}
public static void ReceivedStatusChangedEvent(IntPtr parameter)
{
}
}
}
最佳答案
是的,有可能成为这些问题的受害者。在这种特殊情况下,这很困难。当 native 帧位于托管线程的堆栈上时,主机无法将托管线程切换到不同的操作系统线程,并且由于您立即 p/invoke SleepEx,因此主机切换托管线程的窗口位于两个 p/调用调用。尽管如此,当您需要在同一个操作系统线程上进行 p/调用并且 Thread.BeginThreadAffinity()
存在以涵盖这种情况时,有时仍然是一种令人不快的可能性。
现在是 APC 问题。请记住,操作系统对托管线程一无所知。因此,当 APC 执行可警告的操作时,它将被传递到原始 native 线程中。我不知道 CLR 主机在这些情况下如何创建托管上下文,但如果托管线程与操作系统线程是一对一的,则回调可能会使用托管线程的上下文。
更新 你的新代码现在安全多了,但你在另一个方向上走得太远了:
1) 不需要用线程亲和性包装整个代码。仅包装 确实 需要在同一操作系统线程(通知和 sleep )上运行的两个 p/调用。您是否使用有限超时并不重要,因为您使用线程关联解决的问题是两个 p/调用之间的托管到 OS 线程迁移。无论如何,回调不应假设它在任何特定的托管线程上运行,因为它几乎不能安全地做,而且它应该做的也很少:互锁操作、设置事件和完成 TaskCompletionSources
就是关于它的。
2) GCHandle
是一个简单的 IntPtr
大小的结构,可以比较是否相等。不使用 GCHandle?
,而是使用普通 GCHandle
并与 default(GCHandle)
进行比较。此外,GCHandle?
对我来说在一般原则上看起来很可疑。
3) 关闭服务句柄时通知停止。 SCM 句柄可以保持打开状态,您可能希望保留它以备下次检查。
// Thread.BeginThreadAffinity();
// GCHandle? notifyHandle = null;
var hSCM = OpenSCManager(null, null, (uint)0xF003F);
if (hSCM != IntPtr.Zero)
{
StatusChangedCallbackDelegate changeDelegate = ReceivedStatusChangedEvent;
var notifyHandle = default(GCHandle);
var hService = OpenService(hSCM, serviceName, (uint)0xF003F);
if (hService != IntPtr.Zero)
{
...
notifyHandle = GCHandle.Alloc(notify, GCHandleType.Pinned);
var addr = notifyHandle.AddrOfPinnedObject();
Thread.BeginThreadAffinity();
NotifyServiceStatusChange(hService, (uint)0x00000001, addr);
SleepEx(timeout, true);
Thread.EndThreadAffinity();
CloseServiceHandle(hService);
}
GC.KeepAlive(changeDelegate);
if (notifyHandle != default(GCHandle))
notifyHandle.Free();
CloseServiceHandle(hSCM);
}
另外,为了尽可能安全,如果您的代码要运行很长时间,或者如果您正在编写一个库,您必须使用受限区域和/或 SafeHandles 来确保您的清理例程运行,即使线程中止。查看 BCL 代码跳转的所有环节,例如 System.Threading.Mutex(使用 Reflector 或 CLR 源代码)。至少,使用 SafeHandles 并尝试/最终释放 GCHandle 和结束线程关联。
至于回调相关的问题,这些只是正常多线程问题的一个糟糕案例:死锁、活锁、优先级反转等。这种 APC 回调最糟糕的事情是(除非你阻止整个线程自己直到它发生,在这种情况下,只阻塞 native 代码会更容易)你无法控制它何时发生:你的线程可能深入 BCL 等待 I/O,等待事件发出信号等,并且很难推断程序可能处于的状态。
关于c# - 当您从 C# 进行 P/调用时,异步过程调用如何处理编码(marshal)的委托(delegate)?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/20103529/
我对自定义 CSS 或在将图像作为 Logo 上传到页面时使用编码 block 有疑问。我正在为我的网站使用 squarespace,我需要帮助编码我的 Logo 以使其适合每个页面。一个选项是使用自
如 encoding/json 包文档中所述, Marshal traverses the value v recursively. If an encountered value implement
我必须做一些相当于Java中的iconv -f utf8 -t sjisMS $INPUT_FILE的事情。该命令在 Unix 中 我在java中没有找到任何带有sjisMS的编码。 Java中有Sh
从 PHP 5.3 迁移到 PHP 5.6 后,我遇到了编码问题。我的 MySQL 数据库是 latin1,我的 PHP 文件是 windows-1251。现在一切都显示为“ñëåäíèòå àäðå
我有一个 RScript文件(我们称之为 main.r ),它引用了另一个文件,使用以下代码: source("functions.R") 但是,当我运行 RScript 文件时,它提示以下错误:
我无法设法从 WSDL 创建 RPC/编码风格的代码 - 有谁知道哪个框架可以做到这一点? 带有 adb 和 xmlbeans 映射的 Axis2 无法正常工作(无法处理响应中的肥皂编码)直接使用 X
安装了最新版本的Node.Js()和npm包**(1.2.10)**当我运行 Express 命令来生成项目时,它向我抛出以下错误 buffer.js:240 switch (encoding &
JavaScript中有JSON编码/解码base64编码/解码函数吗? 最佳答案 是的,btoa() 和 atob() 在某些浏览器中可以工作: var enc = btoa("this is so
>>> unicode('восстановление информации', 'utf-16') Traceback (most recent call last): File "", line
我当然熟悉 java.net.URLEncoder 和 java.net.URLDecoder 类。但是,我只需要 HTML 样式的编码。 (我不想将 ' ' 替换为 '+' 等)。我不知道任何只做
有一个非常简单的 SSIS 包: OLE DB Source 通过 View 获取数据(数据库表 nvarchar 或 nchar 中的所有字符串列)。 派生列,用于格式化现有日期并将其添加到数据集(
我正在使用一个在 Node 中进行base64编码的软件,如下所示: const enc = new Buffer('test', 'base64') console.log(enc) 显示: 我正
我试图将带有日语字符的数据插入到 oracle 数据库中。事情是保存在数据库中的是一堆倒置的问号。我该如何解决这个问题 最佳答案 见 http://www.errcode.net/blogs/?p=6
当我在 java 中解压 zip 文件时,我发现文件名中出现了带有重音字符的奇怪行为。 西索: Add File user : L'equipe Technique -- Folder : spec
在网上冲浪我找到了 ExtJS 的 Ext.Gantt 插件,该扩展有一个特殊的编码。任何人都知道如何编码那样或其他复杂的形式。 Encoded Gantt Chart 最佳答案 它似乎被 Dean
我正在用C语言做一个编码任务,我进展顺利,直到读取符号并根据表格分配相应的代码的部分。我必须连接几个代码,直到它们的长度达到 32 位,为此我必须将它们写入一个文件中。这种写入文件的方法给我带来了很多
我有一个外部链接的 javascript 文件。在那个 javascript 里面,我有这个功能: function getMonthNumber(monthName){ monthName = mo
使用mechanize,我检索到一个网页的源页面,其中包含一些非ASCII字符,比如汉字。 代码如下: #using python2.6 from mechanize import Browser b
我有一个包含字母 ø 的文件。当我用这段代码 File.ReadLines(filePath) 读取它时,我得到了一个问号而不是它。 当我像这样添加编码时 File.ReadLines(filePat
如何翻译下面的字符串 H.P. Dembinski, B. K\'{e}gl, I.C. Mari\c{s}, M. Roth, D. Veberi\v{c} 进入 H. P. Dembinski,
我是一名优秀的程序员,十分优秀!