相关疑难解决方法(0)

使用Application.DoEvents()

可以Application.DoEvents()在C#中使用吗?

这个函数是否能够让GUI跟上应用程序的其余部分,就像VB6 DoEvents一样?

c# doevents winforms

263
推荐指数
6
解决办法
17万
查看次数

如何在ASP.NET Web API中使用非线程安全的异步/等待API和模式?

此问题已由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成功使用了类似的单线程模型 …

.net c# asp.net task-parallel-library async-await

33
推荐指数
1
解决办法
1万
查看次数

哪些阻塞操作导致STA线程泵送COM消息?

当一个COM对象在STA线程上实例化时,该线程通常必须实现一个消息泵,以便为来回调用其他线程(见这里).

可以手动泵送消息,或者依赖于某些(但不是全部)线程阻塞操作在等待时自动泵送COM相关消息的事实.文档通常无助于决定哪个是哪个(参见相关问题).

如何确定线程阻塞操作是否会在STA上泵送COM消息?

到目前为止的部分清单:

阻断其业务泵*:

阻止泵送的操作:

*注意Noseratio的答案说,即使是操作泵,也是为非常有限的未公开的COM特定消息集.

c# com interop message-pump

17
推荐指数
1
解决办法
3534
查看次数

MTA控制台应用程序从多个线程调用STA COM对象

虽然有很多关于COM和STA/MTA的问题(例如这里),但大多数人都在讨论具有UI的应用程序.但是,我有以下设置:

  • 控制台应用程序,默认情况下为Multi-Threaded Apartment(Main()显式具有该[MTAThread]属性).
  • 主线程产生一些工作线程.
  • 主线程实例化单线程COM对象.
  • 主线程调用Console.ReadLine(),直到用户点击'q',然后应用程序终止.

几个问题:

  • 许多地方都提到需要为COM对象提供消息泵.我是否需要为主线程手动创建消息泵,或者CLR是否会在新的STA线程上为我创建消息泵,正如这个问题所暗示的那样?
  • 只是为了确保 - 假设CLR自动创建必要的管道,然后我可以使用来自任何工作线程的COM对象而无需显式同步吗?
  • 在性能方面,以下哪项更好:
    • 让CLR负责处理COM对象的编组.
    • 在单独的STA线程上显式实例化对象,并让其他线程通过例如a与之通信ConcurrentQueue.

c# mta com-interop sta

11
推荐指数
3
解决办法
3804
查看次数

CoWaitForMultipleHandles API的行为与记录不符

这是由我正在研究的另一个问题引发的.阅读可能太长了,所以请耐心等待.

显然,在MSDN CoWaitForMultipleHandles没有记录的行为.

下面的代码(基于原始问题)是一个控制台应用程序,它启动一个带有测试Win32窗口的STA线程并尝试发布并抽取一些消息.它做了三个不同的测试CoWaitForMultipleHandles,都没有 COWAIT_WAITALL标志.

测试#1旨在验证这一点:

COWAIT_INPUTAVAILABLE如果设置,如果队列输入存在,则对CoWaitForMultipleHandles的调用将返回S_OK,即使已使用对另一个函数(如PeekMessage)的调用看到(但未删除)输入.

这不会发生,CoWaitForMultipleHandles阻塞并且在发出等待句柄之前不会返回.我不认为任何未决的消息应被视为输入(与同MWMO_INPUTAVAILABLEMsgWaitForMultipleObjectsEx,它按预期工作).

测试#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)

.net c# windows com winapi

10
推荐指数
1
解决办法
5106
查看次数

如何确保我们只有一个实例,并以正确的方式处理

在我写的软件中,我将从外部设备(通过USB连接)读取一些数据.我给出的驱动程序(DLL文件)不是线程安全的,一次只能使用一个实例.我必须在C#中为这些驱动程序编写一个包装器.鉴于我有一个多线程应用程序,我想确保:

  1. 始终只使用一个实例(可能包装器是单件?).
  2. 它可以被处理以释放那里的驱动程序和资源(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)

c# multithreading design-patterns idisposable

7
推荐指数
1
解决办法
625
查看次数

C#/ WSC(COM)互操作中的FatalExecutionEngineError

我即将开始一个用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"类型,这听起来特别可怕(和模糊)!

我拼凑了我认为最小的重现案例,并希望有人可以对这个问题提出一些看法.我还发现了一些似乎可以阻止它的调整,但我无法解释原因.

  1. 创建一个名为"WSCErrorExample"的新的空"ASP.NET Web应用程序"(我在VS 2013/.net 4.5和VS 2010/.net 4.0中完成了这个,它没有区别)

  2. 在项目中添加对"Microsoft.VisualBasic"的引用

  3. 添加一个名为"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# asp.net com vbscript wsc

7
推荐指数
1
解决办法
3295
查看次数

C# 在线程之间编组 COM 对象

我对 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 的线程模型总是让我很困惑:/

c# com multithreading apartments

5
推荐指数
1
解决办法
1975
查看次数

使用 Task.Yield 在实现生产者/消费者模式时克服 ThreadPool 饥饿

回答问题: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

5
推荐指数
1
解决办法
1633
查看次数

等待信号量可能仍在同一线程上执行代码

考虑附加到 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)

c# multithreading semaphore winforms synchronizationcontext

5
推荐指数
0
解决办法
145
查看次数

非 UI 线程上的 WPF 调度程序

根据 MSDN 的说法,它提供了管理线程工作项队列的服务。Dispatcher

一个简单的问题。我想仅用Dispatcher于此目的:作为工作项队列(通过发布Dispatcher.BeginInvoke),我的后台线程将以串行方式提供服务。

我不想让这个线程成为 STA 线程,也不需要在其上发送 Windows 消息。该线程上不会有 UI。

这是合法的使用方式吗Dispatcher

.net c# wpf multithreading begininvoke

4
推荐指数
1
解决办法
4030
查看次数

如何提取COM消息?

我想等待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)

windows delphi com events winapi

3
推荐指数
1
解决办法
372
查看次数

具有多个STA线程的Wpf应用程序仍会阻止用户界面

不久前,我们使用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# wpf ironpython dispatcher sta

2
推荐指数
1
解决办法
1338
查看次数