将原始公共变量作为方法的参数传递以不断检查

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

我几乎可以肯定这个问题以前曾被问过,但可能不会以我要问的方式。

我正在尝试创建一个名为

DAwaiter
的简单方法,该方法等待直到另一个线程或事件使特定的公共 bool 变量为 true。我并不是试图将变量的新副本传递到方法中,而是尝试不断测试特定的已实例化变量的值。
我可以通过让每个任务(例如“主页”任务)都有自己单独的
DAwaiter
副本来解决这个问题,但我很懒,并且不想在可以帮助的情况下单独更新每个命令。

我尝试过使用

ref bool waiter
,但不知道如何有效地使用它。我也尝试过使用
object waiter
,但不确定为什么它不起作用。

这是我的例子:

public class MyForm : Form
{
    ArduinoComms AConnection = new ArduinoComms();

    private void homeButton_Click(object sender, EventArgs e)
    {
        Task.Run(AConnection.Home);
    }
}


public class ArduinoComms 
{
    public SerialPort Port = new SerialPort(/*parameters here*/); //creates and instances an internal serial port.

    Port.DataReceived = new SerialDataReceivedEventHandler(Port_ReceivedData);

    public bool XDone, YDone, Homed, Ready, Stopped, Locked = false; //initializes a lot of bools

    string NewDataContent = "Default newDataContent - should be inaccessible. If you see this, an error has occurred.";
    

    public void Home()
    {
        Homed = false;
        Ready = false;
        XDone = false;
        YDone = false;
        Logger("Beginning home");
        //WriteMyCommand(2); sends command to arduino.
        if (!DAwaiter(Homed, true, 250)) { return; }
        
        Logger("finished home, beginning backoff");
        XDone = false;
        YDone = false;
        //WriteMyCommand(0, backoff);
        //WriteMyCommand(1, backoff);
        if (!DAwaiter(XDone && YDone, true, 250)) { return; }

        Logger("Finished home");
        Ready = true;
    }

    internal bool DAwaiter(object waiter, bool expectedValue, int delay)
    {
        bool waiterVal = (bool)waiter;

        CancellationToken ct = cts.Token;

        Logger($"{Homed}");
        Logger($"Beginning DAwaiter");
        while (waiterVal != expectedValue)
        {
            Logger($"awaiting... {waiterVal}");
            Thread.Sleep(delay);
            if (ct.IsCancellationRequested)
            {
                Logger($"Cancelled DAwaiter.");
                return false;
            }
            waiterVal = waiter;
        }
        Logger($"DAwaiter finished true.");
        return true;
    }

    private void port_DataReceived(object sender, SerialDataReceivedEventArgs e)
    {
        SerialPort spL = (SerialPort)sender; //instances an internal object w/ same type as the event's sender.
        byte[] buf = new byte[spL.BytesToRead]; //instantiates a buffer of appropriate length.
        spL.Read(buf, 0, buf.Length); //reads from the sender, which inherits the data from the sender, which *is* our serial port.
        NewDataContent = $"{System.Text.Encoding.ASCII.GetString(buf)}"; //assembles the byte array into a string.
        Logger($"Received: {NewDataContent}"); //prints the result for debug.
        string[] thingsToParse = NewDataContent.Split('\n'); //splits the string into an array along the newline in case multiple responses are sent in the same message.

        foreach (string thing in thingsToParse) //checks each newline instance individually.
        {
            switch (thing)
            {
                case string c when c.Contains("Home done"): //checks incoming data for the arduino's report phrase "Home done" when it is homed.
                    Homed = true;
                    Logger($"Homed {Homed}");
                    break;

                default: break; //do nothing
            }
        }
    }
    
    public void Logger(string message)
    {
        //log the message.
    }
}

在此示例中,我使用

DAwaiter
等待串行端口另一端的 arduino 响应本地信号。它通过转动公共变量
Homed = true
来注册这个“home”信号。但是,DAwaiter 方法仅传递
Homed
的起始值,这意味着它永远不会停止等待。

  1. 有没有办法让这个传递变量,这样它就会不断测试

    Homed == true
    中的if语句中是否有
    DAwaiter

  2. 这是否可以扩展到多个变量,例如我等待的条件

    Axis1 && Axis2 == true

非常感谢任何建议。

c# methods parameters serial-port
1个回答
0
投票

分析

“Ivan Petrov”提到的问题是,您将 bool 值作为对象传递,然后将其转换为 bool 值。当您执行此装箱操作时,您传递的对象将从引用类型转换为值类型。实现中的另一个问题是,即使您设法仅使用对象将值作为引用类型传递,所使用的机制也是完全不安全的。上述事实是由于多个线程对同一个对象进行读写而引起的,这可能会导致竞争条件。竞争条件是操作系统级别的异常,其中 RAM 内存中的内存地址被执行写入操作的两个或多个线程同时访问。这可能会损坏 RAM 内存中的数据。


解决方案

解决方案是使用 ref 关键字将 bool 值作为参考值传递。 ref 关键字不会更改 bool 对象的类型,而是将指针传递给堆栈中的值。这是线程安全的,因为变量的内存地址是在线程之间传递的。如果您尝试在线程之间传递堆分配的对象,建议使用

lock
语句,将堆分配的地址固定在堆栈上,直到线程完成该对象的过程。

        public static void Main(){
            string thread_name = String.Empty;
            bool value = false;

            // Create and start 10 threads
            for(int i = 0; i < 10; i++)
                new Thread(()=>{ThreadTask(ref thread_name, ref value);}).Start();

            // On the original thread 'lock' the string value and read both the bool and string value
            while(true){
                lock(thread_name){
                    Console.WriteLine(thread_name);
                    Console.WriteLine($"{value}\n\n");
                    Thread.Sleep(500);
                }
            }
        }
        
        public static void ThreadTask(ref string thread_name, ref bool value){
            while(true){
                lock(thread_name){
                    thread_name = $"Thread Id: {Thread.CurrentThread.ManagedThreadId}";
                    value = !value;
                }
            }
        }
最新问题
© www.soinside.com 2019 - 2025. All rights reserved.