您好我想知道是否有与await
语句类似的内容,它与任务一起使用,我可以用c#中的线程实现?
我想做的是:
启动线程A,计算一些数据并将结果放在变量x
上。
之后,变量x
被转移到另一个线程B并同时进行
线程A再次开始计算一些数据,而线程B开始另一个计算结果x
。
更新:好的,似乎有些混乱所以我的描述会更准确:
我使用两个产生数据的传感器。需要以检索SensorA数据的方式检索数据(这需要很长时间),之后必须在另一个线程中检索来自SensorB的数据,而SensorA继续检索另一个数据块。问题是我不能将两个传感器的数据排队在同一队列中,但我需要将两个传感器的数据存储在一个数据结构/对象中。
我的想法就是这样:
您可以假设线程A总是需要比线程B更长的时间
答案 0 :(得分:6)
正如我在评论中所说。这看起来像经典的制作人/消费者,我们可以使用例如一个BlockingCollection
。
这是对该页面样本的轻微修改:
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();
});
然后moreItemsToAdd
就是你需要处理需要关闭此过程所需的任何代码。
答案 1 :(得分:3)
我不确定你为什么要避免使用任务?也许您已经使用旧版的.net了?如果是这样,Damien建议的BlockingCollection也不是一个选择。如果您正在使用&#34;正常&#34;线程,您可以使用等待句柄来表示线程之间的结果。例如,AutoResetEvent。
./configure --target=arm-linux --host=arm-linux && make
编辑:在重置时有轻微的错误,感谢指出Damien - 更新
答案 2 :(得分:3)
如果您可以使用.Net 4.5或更高版本,那么解决此问题的最佳方法是使用TPL的DataFlow组件。
(您必须使用NuGet来安装DataFlow;默认情况下它不是CLR的一部分。)
这是一个示例可编译控制台应用程序,它演示了如何使用DataFlow来执行此操作:
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;
}
}