可以Application.DoEvents()在C#中使用吗?
这个函数是否能够让GUI跟上应用程序的其余部分,就像VB6 DoEvents一样?
此问题已由EF数据上下文 - 异步/等待和多线程触发.我已经回答了那个,但没有提供任何最终的解决方案.
最初的问题是,有许多有用的.NET API(如Microsoft Entity Framework DbContext),它们提供了设计用于的异步方法await,但它们被记录为不是线程安全的.这使它们非常适合在桌面UI应用程序中使用,但不适用于服务器端应用程序.[编辑] 这可能实际上并不适用DbContext,这是微软关于EF6线程安全的声明,自己判断. [/ EDITED]
还有一些已建立的代码模式属于同一类别,例如调用WCF服务代理OperationContextScope(在此处和此处询问),例如:
using (var docClient = CreateDocumentServiceClient())
using (new OperationContextScope(docClient.InnerChannel))
{
return await docClient.GetDocumentAsync(docId);
}
Run Code Online (Sandbox Code Playgroud)
这可能会失败,因为OperationContextScope在其实现中使用线程本地存储.
问题的根源AspNetSynchronizationContext是在异步ASP.NET页面中使用,以便从ASP.NET线程池中使用更少的线程来满足更多HTTP请求.使用时AspNetSynchronizationContext,await可以在与启动异步操作的线程不同的线程上对延续进行排队,同时将原始线程释放到池中,并可用于提供另一个HTTP请求.这大大提高了服务器端代码的可扩展性.该机制在It's All About the SynchronizationContext中有详细描述,必须阅读.因此,虽然没有涉及并发API访问,但潜在的线程切换仍然阻止我们使用上述API.
我一直在考虑如何在不牺牲可扩展性的情况下解决这个问题.显然,恢复这些API的唯一方法是维护可能受线程切换影响的异步调用范围的线程关联.
假设我们有这样的线程亲和力.这些调用中的大多数都是IO绑定的(没有线程).当异步任务处于挂起状态时,它所源自的线程可用于提供另一个类似任务的延续,该结果已经可用.因此,它不应该过多地损害可伸缩性.这种方法并不新鲜,事实上,Node.js成功使用了类似的单线程模型 …
当一个COM对象在STA线程上实例化时,该线程通常必须实现一个消息泵,以便为来回调用其他线程(见这里).
可以手动泵送消息,或者依赖于某些(但不是全部)线程阻塞操作在等待时自动泵送COM相关消息的事实.文档通常无助于决定哪个是哪个(参见相关问题).
如何确定线程阻塞操作是否会在STA上泵送COM消息?
到目前为止的部分清单:
阻断其业务做泵*:
Thread.JoinWaitHandle.WaitOne/ WaitAny/WaitAll(WaitAll不能从一个STA线程虽然称为)GC.WaitForPendingFinalizersMonitor.Enter(因此lock) - 在某些条件下ReaderWriterLock阻止不泵送的操作:
Thread.SleepConsole.ReadKey (在某处读)*注意Noseratio的答案说,即使是操作泵,也是为非常有限的未公开的COM特定消息集.
虽然有很多关于COM和STA/MTA的问题(例如这里),但大多数人都在讨论具有UI的应用程序.但是,我有以下设置:
[MTAThread]属性).几个问题:
ConcurrentQueue.这是由我正在研究的另一个问题引发的.阅读可能太长了,所以请耐心等待.
显然,在MSDN CoWaitForMultipleHandles上没有记录的行为.
下面的代码(基于原始问题)是一个控制台应用程序,它启动一个带有测试Win32窗口的STA线程并尝试发布并抽取一些消息.它做了三个不同的测试CoWaitForMultipleHandles,都没有 COWAIT_WAITALL标志.
测试#1旨在验证这一点:
COWAIT_INPUTAVAILABLE如果设置,如果队列输入存在,则对CoWaitForMultipleHandles的调用将返回S_OK,即使已使用对另一个函数(如PeekMessage)的调用看到(但未删除)输入.
这不会发生,CoWaitForMultipleHandles阻塞并且在发出等待句柄之前不会返回.我不认为任何未决的消息应被视为输入(与同MWMO_INPUTAVAILABLE的MsgWaitForMultipleObjectsEx,它按预期工作).
测试#2旨在验证这一点:
COWAIT_DISPATCH_WINDOW_MESSAGES允许从ASTA或STA中的CoWaitForMultipleHandles分派窗口消息.ASTA中的默认值是没有调度的窗口消息,STA中的默认值只是一小部分特殊的消息调度.该值在MTA中没有意义,将被忽略.
这也不起作用.当CoWaitForMultipleHandles仅使用COWAIT_DISPATCH_WINDOW_MESSAGES标志调用时,它会立即返回错误CO_E_NOT_SUPPORTED(0x80004021).如果它是一个组合COWAIT_DISPATCH_WINDOW_MESSAGES | COWAIT_DISPATCH_CALLS,则呼叫阻止但不抽取任何消息.
测试#3演示了我可以CoWaitForMultipleHandles使用调用线程的Windows消息队列的唯一方法.它是一个组合COWAIT_DISPATCH_WINDOW_MESSAGES | COWAIT_DISPATCH_CALLS | COWAIT_INPUTAVAILABLE.这确实是泵送和发送消息,尽管显然它是一种无证件的行为.
测试代码(可立即运行的控制台应用程序):
using System;
using System.Runtime.InteropServices;
using System.Threading;
using System.Threading.Tasks;
namespace ConsoleTestApp
{
static class Program
{
// Main
static …Run Code Online (Sandbox Code Playgroud) 在我写的软件中,我将从外部设备(通过USB连接)读取一些数据.我给出的驱动程序(DLL文件)不是线程安全的,一次只能使用一个实例.我必须在C#中为这些驱动程序编写一个包装器.鉴于我有一个多线程应用程序,我想确保:
IDisposable?).从一次性单身人士我可以看出意见分歧,单身是否可以IDisposable.也许两者都有更好的解决方案?欢迎任何帮助.
现在我有一个IDisposable单身,如下所示:
using System;
using System.Runtime.InteropServices;
namespace Philips.Research.Myotrace.DataReading.Devices
{
class MyDevice: IDisposable
{
private static volatile MyDeviceInstance;
private static object SyncRoot = new Object();
private bool disposed = false;
private MyDevice()
{
//initialize unmanaged resources here (call LoadLibrary, Initialize, Start etc)
}
public MyDevice GetInstance()
{
if (Instance == null)
{
lock (SyncRoot)
{
if (Instance == null)
{
Instance = new MyDevice();
}
}
}
return Instance;
} …Run Code Online (Sandbox Code Playgroud) 我即将开始一个用VBScript编写的遗留系统的迁移项目.它有一个有趣的结构,因为大部分内容是通过将各种组件编写为"WSC"文件来隔离的,这些文件实际上是以类似COM的方式公开VBScript代码的一种方式.从"核心"到这些组件的边界接口是相当紧凑和众所周知的,所以我希望我能够解决编写新核心并重用WSC,推迟他们的重写.
可以通过添加对"Microsoft.VisualBasic"的引用并调用来加载WSC
var component = (dynamic)Microsoft.VisualBasic.Interaction.GetObject("script:" + controlFilename, null);
Run Code Online (Sandbox Code Playgroud)
其中"controlFilename"是完整的文件路径.GetObject返回类型为"System .__ ComObject"的引用,但可以使用.net的"动态"类型访问属性和方法.
这似乎最初工作正常,但我遇到了很多特定情况的问题 - 我担心这可能发生在其他情况下,或者更糟糕的是,坏事情在很多时候都会发生并被掩盖,等到我最不期望的时候爆炸.
引发的异常是"System.ExecutionEngineException"类型,这听起来特别可怕(和模糊)!
我拼凑了我认为最小的重现案例,并希望有人可以对这个问题提出一些看法.我还发现了一些似乎可以阻止它的调整,但我无法解释原因.
创建一个名为"WSCErrorExample"的新的空"ASP.NET Web应用程序"(我在VS 2013/.net 4.5和VS 2010/.net 4.0中完成了这个,它没有区别)
在项目中添加对"Microsoft.VisualBasic"的引用
添加一个名为"Default.aspx"的新"Web窗体",并将以下内容粘贴到"Default.aspx.cs"的顶部
using System;
using System.IO;
using System.Reflection;
using System.Runtime.InteropServices;
using Microsoft.VisualBasic;
namespace WSCErrorExample
{
public partial class Default : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
var currentFolder = GetCurrentDirectory();
var logFile = new FileInfo(Path.Combine(currentFolder, "Log.txt"));
Action<string> logger = message =>
{
// The try..catch is to avoid IO exceptions when reproducing by requesting …Run Code Online (Sandbox Code Playgroud)我对 C# marshal 的 COM 对象是否在线程之间感到非常困惑。为此,我有一个以任务并行方式加载一组文件的应用程序。我正在使用StaTaskScehduler使用 COM 对象加载文件。加载 COM 对象后,我将该对象存储在中央列表中。
然后我稍后尝试对这些数据执行一些处理,再次使用 STATaskScheduler。但是在这一点上我遇到了一个问题。我收到如下异常:
An unhandled exception of type 'System.Runtime.InteropServices.InvalidComObjectException' occurred in MadCat.exe
Additional information: COM object that has been separated from its underlying RCW cannot be used
Run Code Online (Sandbox Code Playgroud)
现在我的理解是我收到此错误是因为该对象尚未编组到新线程中。我认为这是 C# 为您做的事情?
如何在一个线程中创建一个单元线程 COM 对象,然后从另一个线程使用它?
我在这里吠错树了吗?我什至不应该在我的线程中使用 Sta 公寓吗?我可以保证对象永远不会同时从多个线程访问。任何想法都非常感谢。
编辑:COM 对象定义如下:
[
coclass,
threading( apartment ),
vi_progid( [Namespace.Class] ),
progid( [Namespace.Class].6 ),
version( 6.0 ),
uuid( GUID_C[Class] ),
helpstring( [Class]" Class" )
]
Run Code Online (Sandbox Code Playgroud)
所以根据我的理解,这是一个公寓线程对象,对吗?我刚刚尝试使用未设置公寓状态的修改后的任务调度程序(默认情况下为 MTA?)。当我在一个线程中创建它并从另一个线程使用它时,这个对象似乎确实有效。这是安全的还是会以其他方式回来咬我?
COM 的线程模型总是让我很困惑:/
回答问题:Task.Yield - 实际用途? 我建议使用 Task.Yield 允许池线程被其他任务重用。在这样的模式中:
CancellationTokenSource cts;
void Start()
{
cts = new CancellationTokenSource();
// run async operation
var task = Task.Run(() => SomeWork(cts.Token), cts.Token);
// wait for completion
// after the completion handle the result/ cancellation/ errors
}
async Task<int> SomeWork(CancellationToken cancellationToken)
{
int result = 0;
bool loopAgain = true;
while (loopAgain)
{
// do something ... means a substantial work or a micro batch here - not processing a single byte
loopAgain = /* check …Run Code Online (Sandbox Code Playgroud) c# multithreading threadpool task-parallel-library async-await
考虑附加到 Windows 窗体上的按钮的以下演示代码:
private void button1_Click(object sender, System.EventArgs e)
{
var semaphore = new SemaphoreSlim(0, 1);
Invalidate(); // <-- posts a message that surprisingly will be processed while we're waiting
Paint += onPaint;
semaphore.Wait(1000);
Paint -= onPaint;
void onPaint(object s, PaintEventArgs pe)
{
throw new System.NotImplementedException(); // we WILL hit this!
}
}
Run Code Online (Sandbox Code Playgroud)
尽管我们挂在 UI 线程上的信号量等待上,但是Invalidate()当我们挂在Wait()UI 线程上的- AND(当然)上时,仍然会执行由 发布的绘制消息。
这说明了我试图为其创建失败的单元测试的错误的根本原因 - 不使用任何 Windows 窗体。我已经使用 custom SyncronizationContexts 和TaskSchedulers玩了几个小时,但我无法在同一线程上实现这一点。
我想用伪代码做的是:
[Test] …Run Code Online (Sandbox Code Playgroud) 根据 MSDN 的说法,它提供了管理线程工作项队列的服务。Dispatcher
一个简单的问题。我想仅用Dispatcher于此目的:作为工作项队列(通过发布Dispatcher.BeginInvoke),我的后台线程将以串行方式提供服务。
我不想让这个线程成为 STA 线程,也不需要在其上发送 Windows 消息。该线程上不会有 UI。
这是合法的使用方式吗Dispatcher?
我想等待WebBrowser控件完成导航.所以我创建一个事件,然后我想等待它被设置:
procedure TContoso.NavigateToEmpty(WebBrowser: IWebBrowser2);
begin
FEvent.ResetEvent;
WebBrowser.Navigate2('about:blank'); //Event is signalled in the DocumentComplete event
Self.WaitFor;
end;
Run Code Online (Sandbox Code Playgroud)
然后我在事件中设置DocumentComplete事件:
procedure TContoso.DocumentComplete(ASender: TObject; const pDisp: IDispatch; const URL: OleVariant);
var
doc: IHTMLDocument2;
begin
if (pDisp <> FWebBrowser.DefaultInterface) then
begin
//This DocumentComplete event is for another frame
Exit;
end;
//Set the event that it's complete
FEvent.SetEvent;
end;
Run Code Online (Sandbox Code Playgroud)
问题在于如何等待此事件发生.
第一反应是等待事件被触发:
procedure TContoso.WaitFor;
begin
FEvent.WaitFor;
end;
Run Code Online (Sandbox Code Playgroud)
问题在于DocumentComplete事件永远不会触发,因为应用程序永远不会空闲,以允许COM事件通过.
我的第一反应是忙着睡觉,等待一面旗帜:
procedure TContoso.NavigateToEmpty(WebBrowser: IWebBrowser2);
begin
FIsDocumentComplete := …Run Code Online (Sandbox Code Playgroud) 不久前,我们使用IronPython将Python脚本添加到Wpf应用程序中.起初,它只是"奴隶",因为例如按钮点击调用脚本然后只是运行完成将控制权返回给Wpf.后来我们添加了'master'脚本:脚本在它自己的线程中运行,并控制应用程序的其余部分.这是相当具有挑战性的,但过了一段时间,在现有的SO内容的帮助下,我们看起来很有效.从来没有真正使用它,直到现在,不幸的是事实证明它不能正常工作.核心原因是虽然有两个单独的STA线程(主要的Wpf一个和一个脚本),因此两个不同的Dispatcher实例,主线程似乎被阻止,因为脚本线程在循环中等待主线程完成(响应在脚本线程上处理的按钮单击并在主线程上启动事件).使用具有单独ui窗口的两个线程的全部意义当然不会发生.到底是怎么回事?
更新它是可重复使用最少的代码,所以我链接到它而不是在这里发布伪代码.在创建代码时,我发现当脚本线程创建的窗口未嵌入(设置MainWindow.hostedWin = false)时,不会发生死锁,并且一切都按预期运行.
回应评论所以有3个关注的线程发挥作用.我们称它们为Python,Ui和Process.Python启动Process并等待它完成.进程调用在Ui上调用.在那一点上不应该做任何事情:毕竟,它是阻塞的Python,而不是Ui,而这个结构的重点是Ui不应该与Python交互.好吧,除了它确实以某种方式.哪个是罪魁祸首.在僵局中,Ui坐在那里,PresentationFramework.dll!System.Windows.Interop.HwndHost.OnWindowPositionChanged(System.Windows.Rect rcBoundingBox) + 0x82 bytes而Process坐在WindowsBase.dll!System.Windows.Threading.DispatcherOperation.DispatcherOperationEvent.WaitOne() + 0x2f bytes那里,而Python就在Thread.Sleep.
这里发生了什么,以及如何解决它?
c# ×12
com ×5
.net ×3
asp.net ×2
async-await ×2
sta ×2
winapi ×2
windows ×2
winforms ×2
wpf ×2
apartments ×1
begininvoke ×1
com-interop ×1
delphi ×1
dispatcher ×1
doevents ×1
events ×1
idisposable ×1
interop ×1
ironpython ×1
message-pump ×1
mta ×1
semaphore ×1
threadpool ×1
vbscript ×1
wsc ×1