我试图在后台填充devexpress GridControl(它不是快速过程)。我是这样做的:
...
CreateGrid();
ShowMessageInsteadOfGridControl;
...
FillGrid(dataGrid, other UI params);
...
在网格中写入数据:
private void FillGrid(GridControl data, ...);
{
Task.Factory.StartNew(() =>
{
Application.Current.Dispatcher.Invoke(new Action(() => FillData(gridControl,UIparamns)),
DispatcherPriority.Background);
}).ContinueWith(c => HideUserMessage(UIparamns));
}
当我调用FillData时,会导致UI冻结。我不能使用常用的Task,因为网格从UI填充而且我有#34;调用线程无法访问此对象"。
如何在不冻结UI的情况下在后台进行此类数据混合处理?
答案 0 :(得分:0)
调度程序调用调用将所有内容重新放回UI线程,您需要将操作拆分为可在后台完成的部分以及真正需要的部分在UI线程上完成,比如添加项目,只将这些操作传递给调度程序。
(DispatcherPriority.Background
只是意味着其他具有更高优先级的项目将首先执行,这与后台线程无关,每次调用UI线程的调度程序都会导致执行该操作早晚用UI线程说)
答案 1 :(得分:0)
通常,在处理与UI线程相关的代码时使用Task.Factory.StartNew(...)
是危险的,因为它第一次检查TaskScheduler
时发现没有TaskScheduler
,并且会使用线程池Compute(3)
。使用称为private void Form1_Load(object sender, EventArgs e)
{
Task.Factory.StartNew(A);
}
private static void A() { }
private void Form1_Load(object sender, EventArgs e)
{
Compute(3);
}
private void Compute(int counter)
{
// If we're done computing, just return.
if (counter == 0)
return;
var ui = TaskScheduler.FromCurrentSynchronizationContext();
Task.Factory.StartNew(() => A(counter))
.ContinueWith(t =>
{
Text = t.Result.ToString(); // Update UI with results.
// Continue working.
Compute(counter - 1);
}, ui);
}
private int A(int value)
{
return value; // CPU-intensive work.
}
的函数完成线程的计算后,它将返回,但在这里将其编组回UI线程,然后将使用到目前为止的结果更新UI线程。
第二次和第三次调用Compute,因为SynchronizationContext已封送回UI,它将在UI线程上运行,从而阻塞您的UI。
Task.Run(() => A());
为避免此潜在问题,请改用 private readonly object _myDataLock = new object();
private FastObservableCollection<My20FieldsDataRecord> MyList = new FastObservableCollection<My20FieldsDataRecord>();
private CollectionViewSource MyListCollectionView = new CollectionViewSource();
public MyViewModelConstructor() : base()
{
// Other ctor code
// ...
// assign the data source of the collection views
MyListCollectionView.Source = MyList;
// Setup synchronization
BindingOperations.EnableCollectionSynchronization(MyList, _myDataLock);
}
private async void LoadMyList()
{
// load the list
await Task.Run(async () =>
{
MyList.ReplaceAll(await MyRepository.LoadMyList());
}
);
}
有关更多详细信息,请参阅https://blog.stephencleary.com/2013/08/startnew-is-dangerous.html和/或What is the difference between Task.Run() and Task.Factory.StartNew()上Stephen Stepheny的文章
这是我发现在不阻止UI的情况下在后台加载数据网格的数据的一种方法。
首先,创建一个锁对象,并启用集合同步,然后使用Task.Run()在后台线程上实际加载数据:
public virtual async Task<IList<My20FieldsDataRecord>> LoadMyList()
{
var results = await this.DataContext.TwentyFieldDataRecords
.OrderByDescending(x => x.MyDate).ToListAsync().ConfigureAwait(false);
return results;
}
然后在您的存储库中,您可以编写:
<controls:DataGrid Name="MyListDataGrid" Grid.Row="1"
....
ItemsSource="{Binding MyListCollectionView.View}"
... >
然后,您可以像这样在关联的视图中进行绑定:
turncount = 0
有关详细信息,请参阅: