一周后SemaphoreSlim无法工作

时间:2018-02-02 10:48:52

标签: c# uwp win-universal-app windows-10-universal

更新:FOund错误。我的代码按预期工作,但我的同事在我正在与之通信的设备上添加了一个小代码。结果是它忽略了我的第二个命令,因为它正忙于做其他事情。 修正了它,现在又恢复了工作。

在本周初,我问了一个关于将串口限制只限于一个实例的问题。

有人在那里真的帮了我,并向我提到了SemaphoreSlim。 这当然真的成功了,我认为就是这样。

现在几天后我终于解决了另一个主要问题,在测试时我注意到信号灯不再工作了。

我将我的代码回滚到之前的版本,我刚刚实现了信号量,它确实在哪里工作。 但即便是那段代码也不再适用了。

我在Visual Studio,PC或Raspberry中没有任何改变...

有没有人知道为什么理论上应该工作的东西不再起作用?

非常感谢这里的帮助:)

添加了我正在使用的代码的非常短的版本。 通常我会处理我进入Int或字符串数​​组的数据。 我也有一个CRC16验证,一切都将包含在try / catch块中,以便在它们发生时捕获异常。

但是,这是迄今为止我需要的一切。 如果需要,我会尝试提供更详细的信息。请告诉我。

目前的行为: 第一项任务开始并正常工作。 第二个任务没有开始,之后的每个新任务也都没有开始。

预期行为: 启动第一个任务并完成它。 完成第一个任务后,加载第二个任务并完成它。 当我在那之后开始另一个任务时,它也应该运行它。

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

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

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

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;            
    }
}

1 个答案:

答案 0 :(得分:0)

这看起来应该可以工作,事实上,修改为使用MemoryStream而不是串口的版本可以正常工作。我无法访问您正在使用的I / O API,因此我不了解它们应该如何使用。然而,信号量似乎做得很好。您可以在下方找到示例的简化版本,其中的按钮可以串行或并行运行GetData()

如果信号量存在问题,则只能是您未在Release()内调用finally。否则,您对信号量的使用看起来很好。

要查看信号量是否正常工作,您只需将初始值设定项更改为new SemaphoreSlim(8, 8),然后看到线程相互冲突。

<强> 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:

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:

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:

<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>