有没有办法让任务/线程分多个阶段执行?

问题描述 投票:0回答:1

我正在尝试找出如何改进我对 C# 的使用并解决代码中的一些问题(这是我之前发布的一个更令人恐慌的问题的主题,我可能会因为它不够清晰而将其删除)。然而,我认为这值得从顶部进行更一般的描述:

我正在编写一个 C# 应用程序,该应用程序通过串行端口与 Arduino 连接,控制两个伺服电机。我想要弄清楚的是如何使线程、异步方法或其他系统在方法的特定部分停止并等待它发送到 arduino 的命令完成。

在我目前的结构中,
arduino 通过将字符串写入

SerialPort serialPort
来与 C# 程序进行通信。当接收到新数据时,
serialPort.DataReceived
事件调用方法
serialPort_ReceivedData
。此方法接收新数据并检查其中是否有一些重要短语:

public static void serialPort_ReceivedData(object sender, SerialDataReceivedEventArgs e)
{
SerialPort spL = (SerialPort)sender;
byte[] buf = new byte[spL.BytesToRead];
spL.Read(buf, 0, buf.Length);
newDataContent = $"{System.Text.Encoding.ASCII.GetString(buf)}";
Console.WriteLine($"Received: {newDataContent}");
string[] thingsToParse = newDataContent.Split('\n'); //each command is separated by a newline character, so in case multiple commands are sent close to each other, we separate and iterate through parsing them

            foreach (string thing in thingsToParse)
            {
                switch (thing)
                {
                    case string c when c.Contains("Home done"):
                        homed = true;
                        Console.WriteLine("homed true");
                        break;
    
                    case string a when a.Contains("X done"):
                        xDone = true;
                        Console.WriteLine("xDone true");
                        break;
    
                    case string b when b.Contains("Y done"):
                        yDone = true;
                        Console.WriteLine("yDone true");
                        break;
    
                    default: break; //do nothing
                }
            }
        }

例如,用户在用户界面中选择“Home”命令,该命令设置了

Thread HomeThread = new Thread(() => Home());
,它运行此方法:

public static void Home()
{
    Console.WriteLine("Beginning Home");
    WriteMyCommand(2,0,serialPort); //this method sends the "Home" command to the arduino.
    while(!homed)
    {
        Console.WriteLine("Still homing...");
        Thread.Sleep(250);
    }
    xDone = false; //ensures x and y done flags that are occasionally triggered by Homing are reset before backoff.
    yDone = false;
    Console.WriteLine("Initial homing completed, beginning backoff");
    WriteMyCommand(0, backoff, serialPort);
    WriteMyCommand(1, backoff, serialPort);
    while(!(xDone && yDone))
    {
        Console.WriteLine("Still backing off...");
        Thread.Sleep(250);   
    }
    Console.WriteLine("Backoff completed, unlocking.");
    ready = true;
}

这个系统足够,但不一定好。它不能很好地处理中断并且占用大量内存,特别是在意外调用

HomeThread
的多个实例的情况下。

我尝试过暂时暂停代码执行的替代方法,例如

await
执行虚拟任务(如果仍在运行,请参阅有关该主题的上一个问题),
WaitOne();
,并且我尝试过
await
事件。我确信所有这些方法最终都会起作用,但我的编码技能目前还达不到这些更复杂的技巧的水平。 (作为参考,我仍在努力了解如何制作自己的事件和事件处理程序来解决这个问题)。

有人对处理这个问题有更强有力或更有效的建议吗?我真的很感激任何和所有的建议。

c# multithreading async-await arduino serial-port
1个回答
0
投票

你正在编写并发程序而没有考虑并发性。

据我了解,你的程序是这样做的:

  • 聆听来自 ardoino 的事件
  • 对每个事件以不同的逻辑异步操作
  • 更新状态变量以了解何时可以结束某些操作,例如 home、done

您有几个问题: 如果您打开线程而不考虑已经运行的线程,可能会出现有问题的情况,例如:

  • 开始居家行动
  • x完成
  • 再次开始另一个主页操作,xDone=false
  • y完成(来自原来的家)
  • x完成(来自新家)
  • 第二个主场行动在没有真正完成的情况下完成了

就内存而言,你启动线程与你的系统规格无关,64核的服务器可以打开很多线程,都有效

具有几个核心的小型虚拟机/pod/笔记本电脑将停止获得性能并开始失去性能,因为更多线程打开得相当快。只要有可用的非繁忙线程来完成工作并且工作确实是异步的,更多线程才会有帮助。

幸运的是,C# 有一个名为 TaskPools 的功能,这是针对您的用例的推荐功能,我想说,基本上任务是异步操作,然后由 TaskPool 运行,TaskPool 是一个任务执行工作线程池,C# 自动管理拉取多少线程根据最有效的方式从该池中提取任务。这可能有助于解决您的性能和资源问题。

对于您正确的代码问题,在我看来,虽然您的不同操作可以是异步的,但不能同时运行多个相同的操作。这是假设你的 ardoino 上一次只能运行一个“home”操作。 因此,在您的代码中,您必须确保每种类型仅同时运行一个操作。

您可以使用函数内的锁来做到这一点,在函数开始时锁定,在结束时解锁,以验证一次仅运行一个。

我谈到的概念示例:

using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;

class Program
{
    private static int sharedResource = 0;
    private static readonly object lockObject = new object();

    // Semaphores to ensure only one instance of each action is running at a time
    private static SemaphoreSlim semaphoreAction1 = new SemaphoreSlim(1, 1);
    private static SemaphoreSlim semaphoreAction2 = new SemaphoreSlim(1, 1);

    static async Task Main(string[] args)
    {
        List<Task> tasks = new List<Task>();

        for (int i = 0; i < 5; i++)
        {
            tasks.Add(Task.Run(() => RunActionWithSemaphore(Action1, semaphoreAction1)));
            tasks.Add(Task.Run(() => RunActionWithSemaphore(Action2, semaphoreAction2)));
        }

        await Task.WhenAll(tasks);

        Console.WriteLine($"Final value of shared resource: {sharedResource}");
    }

    static async Task RunActionWithSemaphore(Func<Task> action, SemaphoreSlim semaphore)
    {
        await semaphore.WaitAsync();
        try
        {
            await action();
        }
        finally
        {
            semaphore.Release();
        }
    }

    static async Task Action1()
    {
        for (int i = 0; i < 10; i++)
        {
            await Task.Delay(100); // Simulate some async work

            lock (lockObject)
            {
                sharedResource += 1;
                Console.WriteLine($"Action1 incremented shared resource to {sharedResource}");
            }
        }
    }

    static async Task Action2()
    {
        for (int i = 0; i < 10; i++)
        {
            await Task.Delay(150); // Simulate some async work

            lock (lockObject)
            {
                sharedResource += 2;
                Console.WriteLine($"Action2 incremented shared resource to {sharedResource}");
            }
        }
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.