简体   繁体   中英

Is there an await statement for threads?

Hi I would like to know if there is something similiar to the await statement, which is used with tasks, which I can implement with threads in c#?

What I want to do is:

Start Thread A, compute some data and put the result on variable x . After that variable x is transferred to another thread B and at the same time Thread A starts again to compute some data, while thread B starts another calculation with the result x .

UPDATE: Ok there seems to be some confusion so I will be more accurate in my description:

I use two sensors which produce data. The data needs to be retrieved in such a way that SensorA data is retrieved (which takes a long time) and immediately after that the data from SensorB must be retrieved in another Thread, while SensorA continues retrieving another data block. The problem is i cant queue the data of both sensors in the same queue, but I need to store the data of both sensor in ONE data structure/object.

My idea was like that:

  1. Get Data from Sensor A in Thread A.
  2. Give result to Thread B and restart Thread A.
  3. While Thread A runs again Thread B gets Data from Sensor B and computes the data from Sensor A and B

You can assume that Thread A always needs a longer time than Thread B

As I said in a comment. This looks like classic Producer/Consumer, for which we can use eg a BlockingCollection .

This is a slight modification of the sample from that page:

BlockingCollection<Data> dataItems = new BlockingCollection<Data>(100);

// "Thread B"
Task.Run(() => 
{
    while (!dataItems.IsCompleted)
    {
        Data dataA = null;
        try
        {
            dataA = dataItems.Take();
        }
        catch (InvalidOperationException) { }

        if (dataA != null)
        {
            var dataB = ReadSensorB();
            Process(dataA,dataB);
        }
    }
    Console.WriteLine("\r\nNo more items to take.");
});

// "Thread A"
Task.Run(() =>
{
    while (moreItemsToAdd)
    {
        Data dataA = ReadSensorA();
        dataItems.Add(dataA);
    }
    // Let consumer know we are done.
    dataItems.CompleteAdding();
});

And then moreItemsToAdd is just whatever code you need to have to cope with needing to shut this process down.

I'm not sure why you're avoiding the use of tasks? Maybe you're on an older version of .net? If so, BlockingCollection as Damien suggested is also not an option. If you're using "normal" threads, you can use a waithandle to signal results between threads. For example, an AutoResetEvent .

private int a;
private AutoResetEvent newResult = new AutoResetEvent(false);

private void ThreadA()
{
    while (true)
    {
        a = GetSensorA();
        newResult.Set();
    }
}

private void ThreadB()
{
    int b;

    while (true)
    {
        newResult.WaitOne();
        b = GetSensorB();         // or before "waitone"
        Console.WriteLine(a + b); // do something
    }
}

edit: had slight mistake in there with the reset, thanks for pointing out Damien - updated

If you can use .Net 4.5 or later, then the best way to approach this is to use the DataFlow component of the TPL.

(You must use NuGet to install DataFlow; it's not part of the CLR by default.)

Here's a sample compilable console application which demonstrates how to use DataFlow to do it:

using System;
using System.Threading;
using System.Threading.Tasks;
using System.Threading.Tasks.Dataflow;

namespace SensorDemo
{
    public sealed class SensorAData
    {
        public int Data;
    }

    public sealed class SensorBData
    {
        public double Data;
    }

    public sealed class SensorData
    {
        public SensorAData SensorAData;
        public SensorBData SensorBData;

        public override string ToString()
        {
            return $"SensorAData = {SensorAData.Data}, SensorBData = {SensorBData.Data}";
        }
    }

    class Program
    {
        static void Main()
        {
            var sensorADataSource = new TransformBlock<SensorAData, SensorData>(
                sensorAData => addSensorBData(sensorAData), 
                dataflowOptions());

            var combinedSensorProcessor = new ActionBlock<SensorData>(
                data => process(data), 
                dataflowOptions());

            sensorADataSource.LinkTo(combinedSensorProcessor, new DataflowLinkOptions { PropagateCompletion = true });

            // Create a cancellation source that will cancel after a few seconds.
            var cancellationSource = new CancellationTokenSource(delay:TimeSpan.FromSeconds(20));

            Task.Run(() => continuouslyReadFromSensorA(sensorADataSource, cancellationSource.Token));

            Console.WriteLine("Started reading from SensorA");

            sensorADataSource.Completion.Wait(); // Wait for reading from SensorA to complete.
            Console.WriteLine("Completed reading from SensorA.");

            combinedSensorProcessor.Completion.Wait();
            Console.WriteLine("Completed processing of combined sensor data.");   
        }

        static async Task continuouslyReadFromSensorA(TransformBlock<SensorAData, SensorData> queue, CancellationToken cancellation)
        {
            while (!cancellation.IsCancellationRequested)
                await queue.SendAsync(readSensorAData());

            queue.Complete();
        }

        static SensorData addSensorBData(SensorAData sensorAData)
        {
            return new SensorData
            {
                SensorAData = sensorAData,
                SensorBData = readSensorBData()
            };
        }

        static SensorAData readSensorAData()
        {
            Console.WriteLine("Reading from Sensor A");
            Thread.Sleep(1000); // Simulate reading sensor A data taking some time.
            int value = Interlocked.Increment(ref sensorValue);
            Console.WriteLine("Read Sensor A value = " + value);
            return new SensorAData {Data = value}; 
        }

        static SensorBData readSensorBData()
        {
            Console.WriteLine("Reading from Sensor B");
            Thread.Sleep(100); // Simulate reading sensor B data being much quicker.
            int value = Interlocked.Increment(ref sensorValue);
            Console.WriteLine("Read Sensor B value = " + value);
            return new SensorBData {Data = value};
        }

        static void process(SensorData value)
        {
            Console.WriteLine("Processing sensor data: " + value);
            Thread.Sleep(1000); // Simulate slow processing of combined sensor values.
        }

        static ExecutionDataflowBlockOptions dataflowOptions()
        {
            return new ExecutionDataflowBlockOptions
            {
                MaxDegreeOfParallelism = 1,
                BoundedCapacity        = 1
            };
        }

        static int sensorValue;
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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