需要一种干净的方法来在C#

时间:2018-09-04 00:32:21

标签: c# multithreading

有必要强制我的代码的某些功能在主线程上运行。我们正在谈论单线程黑匣子,如果我从工作线程运行,我的表现是不可接受的。我已经通过保存当前的同步上下文并使用Post调用一个将在主线程中执行的函数来解决这个问题。我知道这是针对GUI的,我不知道为什么它迄今为止一直有效,但是当我们使用必须与之兼容的旧版本的基础软件时,现在已经不一致了。

我已经写了这个基本示例来说明我的问题。我运行一个在新线程中运行的任务,并且在某个时候希望该线程能够要求主线程执行一些操作,然后希望工作线程能够继续执行。

这是当前输出:

Main (ThreadId = 1)
RunTask (ThreadId = 3)
CallBack (ThreadId = 4)  << I want this to be ThreadId 1

如果该解决方案与当前解决方案非常接近,那么任何帮助都将是巨大的,甚至更好,因为我们距离发布版本还有几天的时间,我担心重大的重写可能导致更多的问题。谢谢!

public class Test
{
    internal static SynchronizationContext _context;    
    internal static bool _busy = false;

    static void Main(string[] args)
    {
        Console.WriteLine("Main (ThreadId = " + Thread.CurrentThread.ManagedThreadId + ")");

        SynchronizationContext.SetSynchronizationContext(new SynchronizationContext());

        _context = SynchronizationContext.Current;

        Task.Run(() => RunTask());

        Console.Read();
    }

    public static Task RunTask()
    {
        Console.WriteLine("RunTask (ThreadId = " + Thread.CurrentThread.ManagedThreadId + ")");

        _busy = true;

        _context.Post(new SendOrPostCallback((o) =>
        {
            CallBack(null,
                     EventArgs.Empty);
        }),
                       null);

        while (_busy == true)
        {

        }

        return null;
    }

    public static void CallBack(object sender, EventArgs e)
    {
        Console.WriteLine("CallBack (ThreadId = " + Thread.CurrentThread.ManagedThreadId + ")");
    }
}

2 个答案:

答案 0 :(得分:1)

您可以尝试通过事件将委托传递给主线程:

public class Test
{
    public static BlockingCollection<Action> Handlers = new BlockingCollection<Action>();

    static void Main(string[] args)
    {
        Console.WriteLine("Main (ThreadId = " + Thread.CurrentThread.ManagedThreadId + ")");
        var task = new TaskWrapper();
        task.CallBack += OnCallBack;
        task.Run();

        while (true)
        {
            var action = Handlers.Take();
            action();
        }
    }

    public static void OnCallBack(object sender, Action a)
    {
        Handlers.Add(a);
    }
}

public class TaskWrapper
{
    public event EventHandler<Action> CallBack;

    public TaskWrapper()
    {
        CallBack += (sender, args) => { };
    }

    public void Run()
    {
        Task.Run(() =>
        {
            Console.WriteLine("RunTask (ThreadId = " + Thread.CurrentThread.ManagedThreadId + ")");

            CallBack(this, () => Console.WriteLine("CallBack (ThreadId = " + Thread.CurrentThread.ManagedThreadId + ")"));

            while (true)
            {

            }
        });
    }
}

还请参见this questionthis article,了解如何在控制台应用程序中实现同步上下文。

答案 1 :(得分:1)

谢谢大家的建议。我终于找到了:

https://stackoverflow.com/a/20071498/10312402

和类似的解决方案解决了我的问题。现在是我的示例程序:

public static class DispatcherHelper
{
    public static void DoEvents()
    {
        DispatcherFrame frame = new DispatcherFrame();
        Dispatcher.CurrentDispatcher.BeginInvoke(DispatcherPriority.Background, new DispatcherOperationCallback(ExitFrame), frame);
        Dispatcher.PushFrame(frame);
    }

    private static object ExitFrame(object frame)
    {
        ((DispatcherFrame)frame).Continue = false;
        return null;
    }
}

public class Test
{
    internal static bool _busy = false;

    internal static Dispatcher _dispatcher;

    static void Main(string[] args)
    {
        Console.WriteLine("Main (ThreadId = " + Thread.CurrentThread.ManagedThreadId + ")");

        _dispatcher = Dispatcher.CurrentDispatcher;

        Task.Run(() => RunTask());

        DispatcherHelper.DoEvents();
        DispatcherHelper.DoEvents();
    }

    public static Task RunTask()
    {
        Console.WriteLine("RunTask (ThreadId = " + Thread.CurrentThread.ManagedThreadId + ")");

        _busy = true;

        _dispatcher.Invoke(new Action(CallBack));

        while (_busy == true)
        {

        }

        return null;
    }

    public static void CallBack()
    {
        Console.WriteLine("CallBack (ThreadId = " + Thread.CurrentThread.ManagedThreadId + ")");

        _busy = false;
    }
}

使用该代码,我得到了所需的输出:

Main (ThreadId = 1)
RunTask (ThreadId = 3)
CallBack (ThreadId = 1)

将其放回我的完整程序中也可以。