首页 > 解决方案 > 在主线程上调用委托 - 无 UI

问题描述

我一直在阅读各种各样的问题,这些问题与我正在尝试做的事情相似,但又足够不同,以至于它们似乎并不适用。我拥有的是一个用于创建 CLR 存储过程的 C# 项目。我正在通过多线程来提高 CLR 存储过程的性能。(它有一组嵌套循环,在最里面的循环中,我调用Parallel.ForEach它们在它们自己的线程上运行它们。)嗯,有时完成的处理需要对数据库执行查询。这是使用首先启动存储过程的上下文连接来完成的。这是我的问题。SQL Server 不允许您从子线程访问上下文连接。如果要访问上下文连接,则必须在主线程上执行。

由于这不是 WinForms 项目,因此我无法使用BeginInvoke。(而且我知道 WPF 应用程序有一个类似的命令。)而且我已经看过几篇讨论使用SynchronizationContext来执行此操作的帖子。但是我的主线程没有要引用的SynchronizationContext 。(我认为这是由放置在线程上的第一个控件创建的?)我需要弄清楚如何将执行编组回主线程,足以访问上下文连接。我对使用多线程应用程序仍然有些陌生。因此,如果我的术语使用不佳或不精确,我深表歉意。

谢谢。

编辑:

因此,根据评论和答案,到目前为止,我尝试进行一些更改,但我担心它们要么不起作用,要么我没有得到任何东西。所以我想我会发布一些简化的代码作为我目前正在做的事情的一个例子。(请记住,此示例不起作用,因为查询执行发生在子线程上,并且只有主线程可以使用上下文连接。)

public class MyDatabaseProject
{
    [Microsoft.SqlServer.Server.SqlProcedure]
    public static int MyClrStoredProcedure(...)
    {
        ProcessingEngine engine = new ProcessingEngine();
        engine.SomeQueryEvent += this.HandleSomeQueryEvent;

        ...  // Gather up some data to process.

        DataTable results = engine.Compute(...);

        ...  // Save the computed results DataTable.
    }

    private static void HandleSomeQueryEvent(object Sender, MyEventArgs e)
    {
        SqlConnection contextConn = new SqlConnection("context connection=true");
        contextConn.Open();

        foreach (string query in e.QueriesToExecute)
        {
            // Use the contextConnection to execute the query and store the results in MyEventArgs.
        }
        contextConn.Close();
    }
}

public class ProcessingEngine
{
    public DataTable Compute(...)
    {
        ... // Do stuff

        foreach(var timingIndicator in SomeCollection)
        {
            ... // Do stuff

            Parallel.ForEach(FormulasToProcessNow, new ParallelOptions { MaxDegreeOfParallelism = this.ConcurrencyLevel }, r =>
            {
                ... // Do stuff, including raising "SomeQueryEvent"

                ... // Do stuff with the results of the queries.
            });
        }
    }
}

所以让我感到困惑的是如何以一种可行的方式整合您的建议(例如 ConcurrentQueue 和 AutoResetEvent)。希望这段代码有用。再次感谢。

标签: c#sql-servermultithreadingdatabase-connectionsqlclr

解决方案


根据@0liveradam8 的评论尝试这个食谱。

  1. 创建一个线程安全队列,例如ConcurrentQueue
  2. 将所有线程设置为“运行”;每个线程都会分配一个等待句柄;在这种情况下,AutoResetEvent将是合适的。
  3. 当每个线程必须访问上下文时,将包含三个信息的结构排入队列:aFunc使用上下文、线程的等待句柄和存储Func.
  4. 在循环中的主线程中,将项目出列,调用Func,将结果存储在项目中,然后向等待句柄发出信号。

以上将对上下文的调用编组到主线程,并将结果返回给调用线程。如果一个线程需要多次执行此操作,那没关系,因为您的等待句柄将自动重置为未发出信号,因此可以重用。

使用上下文时线程将被阻塞,这意味着并发性有所降低,但您仍然可能会得到您需要的东西,而且它是安全的。


推荐阅读