如何在 C# 中实现线程关联?

Posted

技术标签:

【中文标题】如何在 C# 中实现线程关联?【英文标题】:How do I implement thread affinity in C#? 【发布时间】:2017-12-14 22:09:33 【问题描述】:

我有一个需要线程关联的第三方 API。我在我的服务应用程序中使用 WCF 来处理来自客户端的请求,然后将这些请求委托给这个 API。由于 WCF 使用线程池来处理请求,因此我尝试使用以下代码解决此问题(使用 SynchronizationContext 类):

using System;
using System.Collections.Generic;
using System.ServiceModel;
using System.Threading;

namespace MyAPIService

  [ServiceBehavior(InstanceContextMode = InstanceContextMode.PerSession)]
  public class MyService: IService
  
      ThirdPartyAPI m_api;
      SynchronizationContext m_context;

      MyService()
      
        m_api = new ThirdPartyAPI();
      

      public bool Connect(string ipaddress, int port)
      
        m_context = (SynchronizationContext.Current == null) ? new SynchronizationContext() : SynchronizationContext.Current;
        return m_api.Connect(ipaddress, port);
      

      public bool Disconnect()
      
        throw new NotImplementedException();
      

      public bool IsConnected()
      
        return Send(() => m_api.IsConnected());
      

      public TResult Send<TResult>(Func<TResult> func)
      
        TResult retval = default(TResult);
        m_context.Send(new SendOrPostCallback((x) =>
        
          retval = func();
        )
        , null);
        return retval;
      

  
 

我认为这将允许我在调用 Connect 的同一线程上执行 IsConnected 方法,但从测试来看并非如此。 IsConnected 仍然在池中的任何线程上执行。我做错了什么?

任何帮助将不胜感激。 非常感谢。

【问题讨论】:

您还需要客户端部分的minimal reproducible example,并确保调试代码以了解您获取 MyService 的新实例是否比您预期的更频繁,或者您的同步上下文管理不起作用。 虽然 SynchronizationContext 是适合这项工作的工具,但它也不是魔法。如果您创建一个新的默认同步上下文 (new SynchronizationContext()),它会将工作发布到线程池(这不是您想要的)。您的外部 API(我想它是一个 COM 对象)是否有某种回调方法?如果是这样,则可以在该回调中捕获同步上下文,然后在其他地方重用它。否则,恐怕你得自己写了 @KevinGosse 第三方 API 实际上不是 com 对象。当您说“自己编写”时,您是指我自己的同步上下文吗? @markf78 是的。编写同步上下文来管理单个线程应该不会太困难。事实上,这里和那里可能有一些可用的实现 【参考方案1】:

默认同步上下文在线程池上执行您的工作(因此,您没有线程亲和性)。

如果您想确保工作始终发布到同一个线程,您需要编写自己的同步上下文。例如:

public class SingleThreadSynchronizationContext : SynchronizationContext

    private readonly BlockingCollection<(SendOrPostCallback callback, object state)> _queue;
    private readonly Thread _processingThread;

    public SingleThreadSynchronizationContext()
    
        _queue = new BlockingCollection<(SendOrPostCallback, object)>();
        _processingThread = new Thread(Process)  IsBackground = true ;
        _processingThread.Start();
    

    public override void Send(SendOrPostCallback d, object state)
    
        using (var mutex = new ManualResetEventSlim())
        
            var callback = new SendOrPostCallback(s =>
            
                d(s);
                mutex.Set();
            );

            _queue.Add((callback, state));
            mutex.Wait();
        
    

    public override void Post(SendOrPostCallback d, object state)
    
        _queue.Add((d, state));
    

    public override SynchronizationContext CreateCopy()
    
        return this;
    

    private void Process()
    
        SetSynchronizationContext(this);

        foreach (var item in _queue.GetConsumingEnumerable())
        
            item.callback(item.state);
        
    

请注意,此同步上下文假定回调中未捕获的异常将使进程崩溃。如果不是这种情况(例如,因为您有一个全局异常处理程序),那么您应该添加一些错误处理(在ProcessSend 中)。

【讨论】:

我不得不改变“_queue.Add((d, state));”到“_queue.Add(new WorkItem(d, state));”。此示例是否使用了 C# 7.0 的新功能? 它使用 ValueTuple 是的,您需要添加 nuget 包才能使用它们。如果不想,可以使用“普通”元组:Tuple.Create(s, state)

以上是关于如何在 C# 中实现线程关联?的主要内容,如果未能解决你的问题,请参考以下文章

如何在Excel中实现关联查找?

如何在 C# 中实现 sdbm 哈希函数?

如何在 C# 中实现 ORM

如何在 C# 中实现 Base64 URL 安全编码?

如何在 C# 中实现单例设计模式? [复制]

如何在c#中实现决策矩阵