简体   繁体   English

SemaphoreSlim一周后不工作

[英]SemaphoreSlim not working after a week

Update: FOund the error. 更新:发现错误。 my code was working as intended but my colleague added a small code to the device i was communicating with. 我的代码按预期工作,但我的同事在与我通信的设备上添加了一个小代码。 The result was that it ignored my second command because it was busy doing something else. 结果是它忽略了我的第二条命令,因为它正忙着做其他事情。 Fixed it and now its working again. 修复了它,现在又可以工作了。

At the beginning of the week I asked a question about restricting access to the serial port to just one instance. 在本周初,我问了一个有关将串行端口访问限制为一个实例的问题。

Someone really helped me there and mentioned the SemaphoreSlim to me. 有人真的帮助了我,并向我提到了SemaphoreSlim This really did the trick at that time and I thought that would be it. 那时确实做到了,我想就是那样。

Now a few days later I finally fixed aynother major problem and while testing it I noticed the semaphore not working anymore. 几天后,我终于解决了另一个主要问题,在测试它时,我注意到该信号灯不再起作用。

I rolled back my code to a previous version where I just implemented the semaphore and where it was definitely working. 我将代码回滚到了以前的版本,在该版本中我刚刚实现了信号灯,并且确实可以正常工作。 But even that code is not working anymore. 但是,即使该代码也不再起作用。

I didn't change anything in my Visual Studio, my PC or my Raspberry... 我没有在Visual Studio,PC或Raspberry中进行任何更改。

Does anybody have an idea why something which should theoretically work doesn't work anymore? 有人知道为什么理论上应该起作用的东西不再起作用了吗?

Would really appreciate the help here :) 非常感谢您的帮助:)

Added a very short version of the code I'm using. 添加了我正在使用的代码的简短版本。 Normally I would process the data I get into Int or string arrays. 通常,我将处理进入Int或字符串数​​组的数据。 I would also have a CRC16 verification and everything would be contained in try/catch blocks to catch exceptions when they occur. 我还将进行CRC16验证,所有内容都将包含在try / catch块中,以在发生异常时捕获异常。

But testwise this is everything I need so far. 到目前为止,这就是我所需要的一切。 I will try to provide more detailed information if needed. 如果需要,我将尝试提供更详细的信息。 Just let me know. 请让我知道。

Current behavior: First task starts and works fine. 当前行为:第一个任务开始并且运行良好。 Second task doesnt start and every new task after that doesn't start either. 第二个任务不会启动,此后的每个新任务也不会启动。

Expected behavior: Start first task and complete it. 预期的行为:开始第一个任务并完成它。 After the first task is done load second task and finish it. 完成第一个任务后,加载第二个任务并完成它。 When I start another Task after that it should also run that. 当我之后启动另一个任务时,它也应该运行。

Mainpage.xaml Mainpage.xaml

public MainPage()
    {
        this.InitializeComponent();

        InitPort();
    }

    private async void getData_Click(object sender, RoutedEventArgs e)
    {
        // Call the async operations.
        // I usually don't call the same operation twice at the same time but it's just a test here.
        // In normal usage the data contains different sizes.

        await getData();

        await getData();
    }

    private async Task getData()
    {
        ReadData readData = new ReadData();

        byte[] readOutput = await readData.ReadParaBlock();

        DisplayTextBox.Text = BitConverter.ToString(readOutput);            
    }

    public async void InitPort()
    {
        string success = await ReadWriteAdapter.Current.Init();
        DisplayTextBox.Text = success;
    }

ReadData.cs ReadData.cs

public class ReadData
{
    private ReadBlock readBlock = new ReadBlock();

    public async Task<byte[]> ReadParaBlock()
    {
        // Load task into the semaphore
        await ReadWriteAdapter.Current.semaphore.WaitAsync();

        // start writing to device
        await readBlock.Write();

        // dropped check of recieved checksum because obsolete for test

        // start reading from device
        byte[] recievedArray = await readBlock.Read();

        // release the task from semaphore
        ReadWriteAdapter.Current.semaphore.Release();

        return recievedArray;            
    }
}

ReadBlock.cs ReadBlock.cs

public class ReadBlock
{

    public async Task<uint> Write()
    {
        // Command sent to device to get data
        byte[] WriteArray = System.Text.Encoding.ASCII.GetBytes("getdata");            

        return await ReadWriteAdapter.Current.WriteAsync(WriteArray);
    }

    public async Task<byte[]> Read()
    {
        byte[] ListenOut = await ReadWriteAdapter.Current.Listen(100);

        // dropped data conversion because obsolete for test

        return ListenOut;
    }
}

ReadWriteAdapter.cs ReadWriteAdapter.cs

public class ReadWriteAdapter
{
    public SemaphoreSlim semaphore { get; private set; }

    private static readonly Object lockObject = new object();
    private static ReadWriteAdapter instance;
    private DataWriter dataWriter = null;
    private DataReader dataReader = null;
    private SerialDevice serialPort = null;

    public static ReadWriteAdapter Current
    {
        get
        {
            if (instance == null)
            {
                lock (lockObject)
                {
                    if (instance == null)
                    {
                        instance = new ReadWriteAdapter();
                    }
                }
            }
            return instance;
        }
    }

    private ReadWriteAdapter()
    {
        this.semaphore = new SemaphoreSlim(1, 1);
    }

    // initialize the serial port and configure it
    public async Task<string> Init()
    {  
        string aqs = SerialDevice.GetDeviceSelector();

        DeviceInformationCollection devices = await DeviceInformation.FindAllAsync(aqs, null);

        if (devices.Any())
        {                
            string deviceId = devices[0].Id;

            serialPort = await SerialDevice.FromIdAsync(deviceId);

            serialPort.WriteTimeout = TimeSpan.FromMilliseconds(1000);
            serialPort.ReadTimeout = TimeSpan.FromMilliseconds(1000);
            serialPort.BaudRate = 19200;
            serialPort.Parity = SerialParity.None;
            serialPort.StopBits = SerialStopBitCount.One;
            serialPort.DataBits = 8;
            serialPort.Handshake = SerialHandshake.None;

            dataWriter = new DataWriter(serialPort.OutputStream);
            dataReader = new DataReader(serialPort.InputStream);                
        }

        return "found port";
    }

    // start to listen to the serial port
    public async Task<byte[]> Listen(uint BufferLength)
    {
        byte[] listen = new byte[BufferLength];

        dataReader = new DataReader(serialPort.InputStream);                    

        listen = await ReadAsync(BufferLength);

        if (dataReader != null)
        {
            dataReader.DetachStream();
            dataReader.Dispose();
            dataReader = null;
        }

        return listen;
    }

    // function to read and interpret the data from serial port
    private async Task<byte[]> ReadAsync(uint ReadBufferLength)
    {
        Task<uint> loadAsyncTask;
        byte[] returnArray = new byte[ReadBufferLength];

        dataReader.InputStreamOptions = InputStreamOptions.Partial;

        loadAsyncTask = dataReader.LoadAsync(ReadBufferLength).AsTask();

        uint bytesRead = await loadAsyncTask;

        if (bytesRead > 0)
        {
            dataReader.ReadBytes(returnArray);
        }            

        return returnArray;
    }

    // write the data using the serial port
    public async Task<uint> WriteAsync(byte[] data)
    {   
        dataWriter.WriteBytes(data);

        Task<uint> storeAsyncTask = dataWriter.StoreAsync().AsTask();

        return await storeAsyncTask;            
    }
}

This looks like it should work, and indeed, a version modified to use a MemoryStream instead of a serial port works just fine. 看起来应该可以正常工作,确实,修改为使用MemoryStream而不是串行端口的版本也可以。 I don't have access to the I/O APIs you're using, so I don't know anything about how they're supposed to be used. 我没有访问您正在使用的I / O API的权限,因此,我对应该如何使用它们一无所知。 The semaphore, however, seems to be doing its job just fine. 但是,该信号灯似乎可以正常工作。 Below you'll find a simplified version of your example, with buttons to run GetData() in serial or in parallel. 在下面,您将找到示例的简化版本,带有用于串行或并行运行GetData()按钮。

If there is a problem with the semaphore, it could only be that you are not calling Release() from inside a finally . 如果信号量有问题,那可能仅仅是因为您没有从finally调用Release() Otherwise, your use of the semaphore looks fine. 否则,您对信号灯的使用会很好。

To see that the semaphore is working, you need only change the initializer to new SemaphoreSlim(8, 8) and see the threads clobber each other. 要查看该信号灯是否正常工作,您只需将初始化程序更改为new SemaphoreSlim(8, 8)然后看一下线程之间的相互干扰。

ReadBlock.cs: ReadBlock.cs:

public class ReadBlock {
    private static int nextString;

    private static readonly string[] strings = {
        "ONE  ", "TWO  ", "THREE", "FOUR ",
        "FIVE ", "SIX  ", "SEVEN", "EIGHT"
    };

    public static async Task<byte[]> ReadParaBlock() {
        var id = Interlocked.Increment(ref nextString) - 1;
        var name = strings[id % strings.Length];

        try
        {
            await ReadWriteAdapter.Current.Semaphore.WaitAsync();
            Trace.WriteLine($"[{name.Trim()}] Entered Semaphore");
            await Write(Encoding.ASCII.GetBytes("TEST_" + name));
            return await Read();
        }
        finally {
            Trace.WriteLine($"[{name.Trim()}] Exiting Semaphore");
            ReadWriteAdapter.Current.Semaphore.Release();
        }
    }

    public static async Task<uint> Write(byte[] bytes) =>
        await ReadWriteAdapter.Current.WriteAsync(bytes);

    public static async Task<byte[]> Read() => await ReadWriteAdapter.Current.Listen(10);
}

ReadWriteAdapter.cs: ReadWriteAdapter.cs:

public class ReadWriteAdapter {
    private static ReadWriteAdapter instance;

    public static ReadWriteAdapter Current
        => LazyInitializer.EnsureInitialized(
            ref instance,
            () => new ReadWriteAdapter());

    private readonly MemoryStream stream = new MemoryStream();

    public SemaphoreSlim Semaphore { get; } = new SemaphoreSlim(1, 1);

    public async Task<string> Init()
        => await Task.FromResult("found port");

    public async Task<byte[]> Listen(uint bufferLength) 
        => await ReadAsync(bufferLength);

    private async Task<byte[]> ReadAsync(uint readBufferLength) {
        await Task.CompletedTask;
        var returnArray = new byte[readBufferLength];
        await stream.ReadAsync(returnArray, 0, returnArray.Length);
        return returnArray;
    }

    public async Task<uint> WriteAsync(byte[] data) {
        stream.SetLength(stream.Capacity);
        stream.Position = 0;
        await Task.Delay(1);
        await stream.WriteAsync(data, 0, data.Length);
        stream.SetLength(data.Length);
        stream.Position = 0;
        return (uint)data.Length;
    }
}

MainWindow.xaml.cs: MainWindow.xaml.cs:

public partial class MainWindow {
    private class TextBoxTraceListener : TraceListener {
        private readonly TextBoxBase _textBox;

        public TextBoxTraceListener(TextBoxBase textBox) 
            => _textBox = textBox;

        public override void WriteLine(string message) 
            => Write(message + Environment.NewLine);

        public override void Write(string message) {
            _textBox.Dispatcher.BeginInvoke(
                DispatcherPriority.Normal,
                new Action(() => _textBox.AppendText(message)));
        }
    }

    public MainWindow() {
        TaskScheduler.UnobservedTaskException +=
            (s, e) => Trace.TraceError(e.ToString());
        InitializeComponent();
        Trace.Listeners.Add(new TextBoxTraceListener(textBox));
        InitPort();
    }

    private async void InitPort() {
        textBox.AppendText(await ReadWriteAdapter.Current.Init());
    }

    private async void OnGetDataInSerialClick(object sender, RoutedEventArgs e) {
        textBox.Clear();
        for (var i = 0; i < 8; i++) await GetData();
    }

    private async void OnGetDataInParallelClick(object sender, RoutedEventArgs e) {
        textBox.Clear();
        await Task.WhenAll(Enumerable.Range(0, 8).Select(i => GetData()));
    }

    private async Task GetData() {
        await Task.Delay(50).ConfigureAwait(false); // Get off the UI thread.
        Trace.WriteLine(Encoding.ASCII.GetString(await ReadBlock.ReadParaBlock()));
    }
}

MainWindow.xaml: MainWindow.xaml:

<Window x:Class="WpfTest2.MainWindow"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
  <DockPanel LastChildFill="True">
    <Button DockPanel.Dock="Bottom" Click="OnGetDataInSerialClick"
                Content="Load Data in Serial" />
    <Button DockPanel.Dock="Bottom" Click="OnGetDataInParallelClick"
                Content="Load Data in Parallel" />
    <TextBox x:Name="textBox" TextWrapping="Wrap" />
  </DockPanel>
</Window>

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM