C# Windows IoT上的多线程导致线程关闭

C# Windows IoT上的多线程导致线程关闭,c#,windows,multithreading,iot,C#,Windows,Multithreading,Iot,我对为Windows IoT编写应用程序比较陌生。我有一个Windows IoT后台应用程序,我想从主线程生成三个独立的线程。(我希望它们都在单独的后台线程中运行的原因是,它们将要做的一些工作可能很耗时,所以我显然不想阻止任何事情) 第一个线程运行一个小型web服务器 第二个线程正在侦听Raspberry PI上的GPIO引脚 第三个线程是通过I2C监听设备 由于某种原因,我似乎无法使所有三个线程保持打开状态。以下是我在StartupTask中的代码: public sealed class S

我对为Windows IoT编写应用程序比较陌生。我有一个Windows IoT后台应用程序,我想从主线程生成三个独立的线程。(我希望它们都在单独的后台线程中运行的原因是,它们将要做的一些工作可能很耗时,所以我显然不想阻止任何事情)

第一个线程运行一个小型web服务器

第二个线程正在侦听Raspberry PI上的GPIO引脚

第三个线程是通过I2C监听设备

由于某种原因,我似乎无法使所有三个线程保持打开状态。以下是我在StartupTask中的代码:

public sealed class StartupTask : IBackgroundTask
{
    private static BackgroundTaskDeferral _Deferral = null;
    public async void Run(IBackgroundTaskInstance taskInstance)
    {
        _Deferral = taskInstance.GetDeferral();

        // do some stuff on the main thread here...

        // thread 1
        var webserver = new TestWebserver();
        await ThreadPool.RunAsync(workItem =>
        {
            webserver.Start();
        });

        // thread 2
        var masterEventListener = new MasterEventListener();
        await ThreadPool.RunAsync(workItem =>
        {
            masterEventListener.Start();
        });

        // thread 3
        var i2cEventListener = new I2CEventListener();
        await ThreadPool.RunAsync(workItem =>
        {
            i2cEventListener.Start();
        });        
    }
}
以下是第一个线程的shell:

internal class TestWebserver
{
    private const uint BufferSize = 8192;
    public async void Start()
    {
        var listener = new StreamSocketListener();
        await listener.BindServiceNameAsync(8081);

        listener.ConnectionReceived += async (sender, args) =>
        {
            var request = new StringBuilder();
            using (var input = args.Socket.InputStream)
            {
                var data = new byte[BufferSize];
                IBuffer buffer = data.AsBuffer();
                var dataRead = BufferSize;

                while (dataRead == BufferSize)
                {
                    await input.ReadAsync(buffer, BufferSize, InputStreamOptions.Partial);
                    request.Append(Encoding.UTF8.GetString(data, 0, data.Length));
                    dataRead = buffer.Length;
                }
            }

            using (var output = args.Socket.OutputStream)
            {
                using (var response = output.AsStreamForWrite())
                {
                    string html = "TESTING RESPONSE";
                    using (var bodyStream = new MemoryStream(html))
                    {
                        var header = $"HTTP/1.1 200 OK\r\nContent-Length: {bodyStream.Length}\r\n\r\nConnection: close\r\n\r\n";
                        var headerArray = Encoding.UTF8.GetBytes(header);
                        await response.WriteAsync(headerArray, 0, headerArray.Length);
                        await bodyStream.CopyToAsync(response);
                        await response.FlushAsync();
                    }
                }
            }
        };
    }
}
下面是第二个线程的shell:

internal class MasterEventListener
{
    public void Start()
    {
        GpioController gpio = GpioController.GetDefault();
        GpioPin gpioPin = gpio.OpenPin(4); // pin4

        if (gpioPin.IsDriveModeSupported(GpioPinDriveMode.InputPullUp))
        {
            gpioPin.SetDriveMode(GpioPinDriveMode.InputPullUp);
        }
        else
        {
            gpioPin.SetDriveMode(GpioPinDriveMode.Input);
        }

        gpioPin.Write(GpioPinValue.High);
        gpioPin.DebounceTimeout = TimeSpan.FromMilliseconds(25);
        gpioPin.ValueChanged += Pin_ValueChanged;
    }

    private void Pin_ValueChanged(GpioPin sender, GpioPinValueChangedEventArgs args)
    {
        bool value = sender.Read() == GpioPinValue.High;

        if (value)
        {
            Debug.WriteLine("OPEN!");
        }
        else
        {
            Debug.WriteLine("CLOSED!");
        }
    }
}
这是第三条线的外壳:

internal class I2CEventsListener
{
    public async void Start()
    {
        string aqs = I2cDevice.GetDeviceSelector();
        DeviceInformationCollection dis = await DeviceInformation.FindAllAsync(aqs);

        I2CThreadListener(dis);
    }

    private async void I2CThreadListener(DeviceInformationCollection dis)
    {
        while(true)
        {
            var settings = new I2cConnectionSettings(3); // I2C address 3
            settings.BusSpeed = I2cBusSpeed.FastMode;
            settings.SharingMode = I2cSharingMode.Shared;

            using (I2cDevice device = await I2cDevice.FromIdAsync(dis[0].Id, settings))
            {
                if (device != null)
                {
                    try
                    {
                        byte[] writeBuffer = Encoding.ASCII.GetBytes("000000");
                        byte[] readBuffer = new byte[7];

                        device.Write(writeBuffer);
                        device.Read(readBuffer);

                        var str = Encoding.Default.GetString(readBuffer);
                        if (str != null && str.Trim().Length == 7 && Convert.ToInt32(readBuffer[0]) > 0)
                        {
                            Debug.WriteLine("RESULTS! '" + str + "'");
                        }
                    }
                    catch (Exception ex)
                    {
                        Debug.WriteLine(ex.Message);
                        Debug.WriteLine(ex.StackTrace);
                    }
                }
            }
            Thread.Sleep(100);
        }               
    });
}
如果我注释掉这两个线程中的任何一个,剩下的线程将无限期地运行,并且工作正常

如果我注释掉一个线程,剩下的两个线程会完美地工作(有时)大约30秒,然后其中一个线程会以如下消息终止:

The thread 0xad0 has exited with code 0 (0x0).
我的日志中从未收到任何错误消息,因此我不相信会抛出任何类型的错误

我看到了我期望的结果——只要我只运行其中一个线程。但一旦我有多个线程一起运行,就会产生问题

任何指示都将不胜感激


谢谢大家…

基于您的更新代码,尽管I2C线程中的while循环阻止Run方法退出,因此本地类变量(webserver等)将始终有效。以及I2C线程在while循环中的所有必要变量,以便它们始终有效。但是像
listener
gpio
gpioPin
这样的局部变量将由GC收集,然后在方法执行完成后不再有效。然后线程将退出

为了解决这个问题,我对你的代码做了一些编辑,它很有效。您可以尝试一下:

public sealed class StartupTask : IBackgroundTask
    {
        private static BackgroundTaskDeferral _Deferral = null;
        private static TestWebserver webserver = null;
        private static MasterEventListener masterEventListener = null;
        private static I2CEventsListener i2cEventListener = null;

        public async void Run(IBackgroundTaskInstance taskInstance)
        {
            _Deferral = taskInstance.GetDeferral();

            // do some stuff on the main thread here...

            // thread 1
            webserver = new TestWebserver();
            await Windows.System.Threading.ThreadPool.RunAsync(workItem =>
            {
                webserver.Start();
            });

            // thread 2
            masterEventListener = new MasterEventListener();
            await Windows.System.Threading.ThreadPool.RunAsync(workItem =>
            {
                masterEventListener.Start();
            });

            // thread 3
            i2cEventListener = new I2CEventsListener();
            await Windows.System.Threading.ThreadPool.RunAsync(workItem =>
            {
                i2cEventListener.Start();
            });

            Debug.WriteLine("The Run method exit...");
        }

        internal class TestWebserver
        {
            private StreamSocketListener listener = null;
            private const uint BufferSize = 8192;
            public async void Start()
            {
                listener = new StreamSocketListener();
                await listener.BindServiceNameAsync("8081");

                listener.ConnectionReceived += async (sender, args) =>
                {
                    var request = new StringBuilder();
                    using (var input = args.Socket.InputStream)
                    {
                        var data = new byte[BufferSize];
                        IBuffer buffer = data.AsBuffer();
                        var dataRead = BufferSize;

                        while (dataRead == BufferSize)
                        {
                            await input.ReadAsync(buffer, BufferSize, InputStreamOptions.Partial);
                            request.Append(Encoding.UTF8.GetString(data, 0, data.Length));
                            dataRead = buffer.Length;
                        }
                    }

                    using (var output = args.Socket.OutputStream)
                    {
                        using (var response = output.AsStreamForWrite())
                        {
                            string html = "TESTING RESPONSE";
                            using (var bodyStream = new MemoryStream(Encoding.ASCII.GetBytes(html)))
                            {
                                var header = $"HTTP/1.1 200 OK\r\nContent-Length: {bodyStream.Length}\r\n\r\nConnection: close\r\n\r\n";
                                var headerArray = Encoding.UTF8.GetBytes(header);
                                await response.WriteAsync(headerArray, 0, headerArray.Length);
                                await bodyStream.CopyToAsync(response);
                                await response.FlushAsync();
                            }
                        }
                    }
                };
            }
        }

        internal class MasterEventListener
        {
            private GpioController gpio = null;
            private GpioPin gpioPin = null;

            public void Start()
            {

                gpio = GpioController.GetDefault();
                gpioPin = gpio.OpenPin(4); // pin4

                if (gpioPin.IsDriveModeSupported(GpioPinDriveMode.InputPullUp))
                {
                    gpioPin.SetDriveMode(GpioPinDriveMode.InputPullUp);
                }
                else
                {
                    gpioPin.SetDriveMode(GpioPinDriveMode.Input);
                }

                gpioPin.Write(GpioPinValue.High);
                gpioPin.DebounceTimeout = TimeSpan.FromMilliseconds(25);
                gpioPin.ValueChanged += Pin_ValueChanged;
            }

            private void Pin_ValueChanged(GpioPin sender, GpioPinValueChangedEventArgs args)
            {
                bool value = sender.Read() == GpioPinValue.High;

                if (value)
                {
                    Debug.WriteLine("OPEN!");
                }
                else
                {
                    Debug.WriteLine("CLOSED!");
                }
            }
        }

        internal class I2CEventsListener
        {
            public async void Start()
            {
                string aqs = I2cDevice.GetDeviceSelector();
                DeviceInformationCollection dis = await DeviceInformation.FindAllAsync(aqs);

                I2CThreadListener(dis);
            }

            private async void I2CThreadListener(DeviceInformationCollection dis)
            {
                var settings = new I2cConnectionSettings(3); // I2C address 3
                settings.BusSpeed = I2cBusSpeed.FastMode;
                settings.SharingMode = I2cSharingMode.Shared;

                I2cDevice device = await I2cDevice.FromIdAsync(dis[0].Id, settings);
                if (null == device)
                {
                    Debug.WriteLine("Get I2C device is NULL. Exiting...");
                }

                byte[] writeBuffer = Encoding.ASCII.GetBytes("000000");
                byte[] readBuffer = new byte[7];

                while (true)
                {
                    try
                    {
                        device.Write(writeBuffer);
                        device.Read(readBuffer);

                        var str = Encoding.Default.GetString(readBuffer);
                        if (str != null && str.Trim().Length == 7 && Convert.ToInt32(readBuffer[0]) > 0)
                        {
                            Debug.WriteLine("RESULTS! '" + str + "'");
                        }
                    }
                    catch (Exception ex)
                    {
                        Debug.WriteLine(ex.Message);
                        Debug.WriteLine(ex.StackTrace);
                    }

                    Thread.Sleep(100);
                }
            }
        }


    }
建议为使用线程池创建短期的工作项。请参阅“”


对于长时间运行的
任务,对于您的情况,如果您的三个线程没有相互通信,您可以分别为每个任务创建一个。

可能是因为异常。它是否在调试控制台上显示异常消息?但显然,您必须在发出请求之前检查该消息。没有出现异常。我想这与我如何设置线程有关?我对整个async/await概念有点陌生,所以根本不会让我感到震惊!您的代码看起来比需要的复杂多了。您可以使用
Task.Run(async()=>{//您需要在后台运行的永久循环})从主方法生成两个任务
@Mike如果没有异常,您可以检查它是否挂起。如何确定“线程0xe80”是I2C的第二个线程?谢谢!我从来没有想到这是局部变量的垃圾收集,但你是对的。这解决了我的问题!我不确定我自己能想出这个,所以非常感谢你抽出时间!!!