在C#的同一个异步线程中运行不同的任务[重复]

Posted

技术标签:

【中文标题】在C#的同一个异步线程中运行不同的任务[重复]【英文标题】:Run different task in same asynchronous thread in C# [duplicate] 【发布时间】:2021-04-18 02:20:22 【问题描述】:

我不得不问,因为我找不到与 UI BeginInvoke 异步完成相同的方式。

我在 Winforms 上运行的示例程序和所有委托方法都在主 UI 线程中调用。我记录了状态,发现它在不同 BeginInvoke 的同一线程上运行。

来自 Winforms UI 的日志:

13/01/2021 11:57:23 [fnCmdInProgressAction] - [fnCmdInProgressAction] Background: False, Thread Pool: False, Thread ID: 1
13/01/2021 11:57:23 [fnCmdInProgressAction] - [fnCmdInProgressAction] Background: False, Thread Pool: False, Thread ID: 1
13/01/2021 11:57:23 [fnCmdInProgressAction] - [fnCmdInProgressAction] Background: False, Thread Pool: False, Thread ID: 1
13/01/2021 11:57:23 [fnCmdInProgressAction] - [fnCmdInProgressAction] Background: False, Thread Pool: False, Thread ID: 1
13/01/2021 11:57:23 [SendNextCommand] - [SendNextCommand] Background: False, Thread Pool: False, Thread ID: 1
13/01/2021 11:57:23 [fnCmdInProgressAction] - [fnCmdInProgressAction] Background: False, Thread Pool: False, Thread ID: 1
13/01/2021 11:57:24 [fnCmdInProgressAction] - [fnCmdInProgressAction] Background: False, Thread Pool: False, Thread ID: 1
13/01/2021 11:57:24 [SendNextCommand] - [SendNextCommand] Background: False, Thread Pool: False, Thread ID: 1
13/01/2021 11:57:24 [fnCmdInProgressAction] - [fnCmdInProgressAction] Background: False, Thread Pool: False, Thread ID: 1
13/01/2021 11:57:24 [fnCmdInProgressAction] - [fnCmdInProgressAction] Background: False, Thread Pool: False, Thread ID: 1
13/01/2021 11:57:24 [SendNextCommand] - [SendNextCommand] Background: False, Thread Pool: False, Thread ID: 1

我想将其转换为类库,以便作为 DLL 集成到我的实际应用程序中。但我找不到如何让fnCmdInProgressActionSendNextCommand 方法在同一个线程中运行。因为,我有一个链命令,它需要排队,因为对象值将被清除并允许下一个命令执行到串行端口。

来自我的类​​库的日志:

13/01/2021 11:30:51 [fnCmdInProgressAction] - [fnCmdInProgressAction] Background: True, Thread Pool: True, Thread ID: 6
13/01/2021 11:30:52 [fnCmdInProgressAction] - [fnCmdInProgressAction] Background: True, Thread Pool: True, Thread ID: 9
13/01/2021 11:30:55 [fnCmdInProgressAction] - [fnCmdInProgressAction] Background: True, Thread Pool: True, Thread ID: 7
13/01/2021 11:30:55 [fnCmdInProgressAction] - [fnCmdInProgressAction] Background: True, Thread Pool: True, Thread ID: 6
13/01/2021 11:30:56 [SendNextCommand] - [SendNextCommand] Background: True, Thread Pool: True, Thread ID: 7
13/01/2021 11:30:56 [fnCmdInProgressAction] - [fnCmdInProgressAction] Background: True, Thread Pool: True, Thread ID: 9
13/01/2021 11:30:57 [fnCmdInProgressAction] - [fnCmdInProgressAction] Background: True, Thread Pool: True, Thread ID: 7
13/01/2021 11:30:58 [SendNextCommand] - [SendNextCommand] Background: True, Thread Pool: True, Thread ID: 6

我尝试过 ThreadPool、Delegate.BeginInvoke、Thread Task。但仍然导致不同的线程。以下是我尝试过的示例:

private delegate void CmdInProgressAction(bool state, Tools.CommandReply cr);
private void fnCmdInProgressAction(object param)

    if (cr != null)
    
        m_CmdInProgress = cr;
        //.......
    
    m_CmdInProgress = null;

// Code from Winform UI BeginInvoke
private void SetCmdInProgress(bool state, CommandReply cr)

    CmdInProgressAction fn = null;
    object[] param = new object[]  state, cr ;
    fn = new CmdInProgressAction(fnCmdInProgressAction);
    BeginInvoke(fn, param);

// My try
private void SetCmdInProgress(bool state, CommandReply cr)

    //=========================  TRY 1   =================================================
    new Task(() =>  fnCmdInProgressAction(state, cr); ).Start();
    //=========================  TRY 2   =================================================
    CmdInProgressAction fn = fnCmdInProgressAction;
    fn.BeginInvoke(state, cr, null, null);
    //=========================  TRY 3   =================================================
    new Thread(new ThreadStart(() => fnCmdInProgressAction(state, cr))).Start();
    //=========================  TRY 4   =================================================
    ThreadPool.QueueUserWorkItem(fnCmdInProgressAction, new object[]  state, cr );

public void SendNextCommand(TXNextCommand tnxc)

    Thread thread = Thread.CurrentThread;
    string message = $"[SendNextCommand] Background: thread.IsBackground, Thread Pool: thread.IsThreadPoolThread, Thread ID: thread.ManagedThreadId";
    WriteLog.ProcessLog(message);
    if (m_CmdInProgress == null)
    
        m_CmdInProgress = tnxc.mcr;
        serial.SendCommand(m_CmdInProgress);
    

private string HandleReply(bool GoodResult, Tools.CommandReply cr)

        int nextChannel = cr.m_tag + 1;
        TXNextCommand tnxc= new TXNextCommand(new TXReadCassetteDeno($"Read Cassette nextChannel Deno", "RV-1X2", 5, channel: nextChannel));
        tnxc.mcr.SetReplyHandlerDelegate(HandleReply);
        /// Below is code from Winforms UI Async on main thread
        dSendNextCommand fn = new dSendNextCommand(SendNextCommand);
        object[] param = new object[]  nctt ;
        BeginInvoke(fn, param);

在同一线程中从HandleReplyfnCmdInProgressAction 运行委托的正确方法是什么,所以SendNextCommand 调用,m_CmdInProgress 已经为空,因此它可以发送下一个命令。 Winforms UI 没有问题,因为它就像在同一个线程上排队调用。我如何在类库上应用它,因为我尝试过的不能达到我想要的。我读过关于任务异步编程,TPL。我不能使用异步,因为它会弄乱串行类对象上的委托。

【问题讨论】:

我可以澄清一下吗?您正在寻找一种将工作推送到专用线程的方法,类似于 winforms 中的 UI 线程 - 对吗?您可以通过同步委托队列简单地拥有自己的工作线程吗?另外:是否上面有一个 UI 线程,即使在库中,您也可以将其用于线程关联?或者它必须在没有任何假设的情况下工作吗? @MarcGravell,没错。我希望它有一个类似于 UI 线程的专用线程。目前,每次调用发生时,ManagedThreadId 将具有不同的 ID,并且下一个命令只是触发并跳过,因为对象不为空(已完成)。 有一个名为ConcurrentExclusiveSchedulerPair (1) 的内置调度程序,它有一个ExclusiveScheduler (1) 属性。有了它,您可以创建一个TaskFactory @PeterCsala,让我先动手。这是我第一次听说ConcurrentExclusive 【参考方案1】:

回答我的问题。在Peter Csala 的推荐下,我正在使用ConcurrentExclusiveSchedulerPair。现在该命令可以根据需要执行。这是我所做的。

static ConcurrentExclusiveSchedulerPair taskSchedulerPair = new ConcurrentExclusiveSchedulerPair();
TaskFactory exclusiveTaskFactory = new TaskFactory(taskSchedulerPair.ExclusiveScheduler);

private void SetCmdInProgress(bool state, Tools.CommandReply cr)

    //====================================================================================
    exclusiveTaskFactory.StartNew(() =>  fnCmdInProgressAction(new object[]  state, cr ); );
    //====================================================================================

private string HandleReply(bool GoodResult, Tools.CommandReply cr)

    int nextChannel = cr.m_tag + 1;
    TXCommand.TXNextCommand tnxc = new TXCommand.TXNextCommand(new TXCommand.TXReadCassetteDeno($"Read Cassette nextChannel Deno", "RD/9X27", 5, channel: nextChannel));
    tnxc.mcr.SetReplyHandlerDelegate(HandleReply);
    //====================================================================================
    exclusiveTaskFactory.StartNew(() =>  SendNextCommand(new object[]  tnxc ); );
    //====================================================================================

我仍然有一些不同的线程 id;但是,通过使用ConcurrentExclusiveSchedulerPair,它可以将任务排队,然后按照所需的流程继续执行另一个任务

【讨论】:

以上是关于在C#的同一个异步线程中运行不同的任务[重复]的主要内容,如果未能解决你的问题,请参考以下文章

如何在c#控制台应用程序中自动(强制)停止当前线程[重复]

C#在遍历文件结构时如何判断所有线程或任务何时完成[重复]

如何在c#中正确实现等待异步[重复]

异步方法运行速度太快,无法在主线程上更新我的列表框 [重复]

如何在后台线程中按顺序运行任务[重复]

Android Service 和线程中运行的重复性任务