在我的 WPF 项目中。我有:
public partial class MainWindow : Window
{
ObservableCollection<Calls> items = new ObservableCollection<Calls>();
public MainWindow()
{
InitializeComponent();
icTodoList.ItemsSource = items;
this.DataContext = new MainViewModel();
}
icTodoList
是一个 ItemsControl
,我想为其添加两列。
<DockPanel>
<ItemsControl Height="300" Name="icTodoList" ItemsSource="{Binding Calls}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="60"/>
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<TextBlock Text="{Binding DialingNumber, Mode=OneWay, FallbackValue=' '}" Grid.Column="0"/>
<TextBlock Text="{Binding DialResult, Mode=OneWay, FallbackValue=' '}" Grid.Column="1"/>
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl></DockPanel>
对于Calls
类,我们有
public class Calls : NotifyUIBase
{
private string dialingNumber;
public string DialingNumber
{
get { return dialingNumber; }
set
{
dialingNumber = value;
RaisePropertyChanged();
}
}
public string dialResult;
public string DialResult {
get { return dialResult; }
set
{
dialResult = value;
RaisePropertyChanged();
}
}
}
NotifyUIBase
继承自INotifyPropertyChanged
,它包含RaisePropertyChanged
属性,此处忽略写入以节省空间。
现在,我通过单击“开始”按钮获得了一个生产者-消费者流程。
private async void Start_Click(object sender, RoutedEventArgs e)
{
var producer = Producer();
var consumer = Consumer();
await Task.WhenAll(producer, consumer);
}
在consumer
方法中,我们更新ItemsControl。
async Task Consumer()
{
try
{
var executionDataflowBlockOptions = new ExecutionDataflowBlockOptions
{
MaxDegreeOfParallelism = 50
};
var consumerBlock = new ActionBlock<AppointmentReminder>(
remainder =>
{
Calls c = new Calls();
c.DialingNumber = "number..";
c.DialResult = "result...;
items.Add(c);
},
executionDataflowBlockOptions);
bufferBlock.LinkTo(
consumerBlock, new DataflowLinkOptions { PropagateCompletion = true });
await Task.Delay(500);
}
但是我有一个异常(exception):
This type of CollectionView does not support changes to its SourceCollection from a thread different from the Dispatcher thread.
事情发生在线路上:
items.Add(c);
我猜是线程问题,那么如何解决呢?
最佳答案
您已经将一些选项传递给您的 ActionBlock
;您可以在那里轻松指定 UI 调度程序:
var executionDataflowBlockOptions = new ExecutionDataflowBlockOptions
{
TaskScheduler = TaskScheduler.FromCurrentSynchronizationContext(),
};
请注意,它不再并行运行。如果您的操作代码很简单(创建单个对象并设置几个属性),那么这应该足够了。但是,如果对象创建是 CPU 密集型的,您可能希望保持并行性:
var transformOptions = new ExecutionDataflowBlockOptions
{
MaxDegreeOfParallelism = 50
};
var actionOptions = new ExecutionDataflowBlockOptions
{
TaskScheduler = TaskScheduler.FromCurrentSynchronizationContext(),
};
var transformBlock = new TransformBlock<AppointmentReminder, Calls>(reminder =>
{
Calls c = new Calls();
c.DialingNumber = "number..";
c.DialResult = "result...;
return c;
}, transformOptions);
var consumerBlock = new ActionBlock<Calls>(c =>
{
items.Add(c);
}, actionOptions);
var linkOptions = new DataflowLinkOptions { PropagateCompletion = true };
bufferBlock.LinkTo(transformBlock, linkOptions);
transformBlock.LinkTo(consumerBlock, linkOptions);
关于c# - 在 TPL 中安全添加集合,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/26511997/