Обычно опасно использовать Task.Factory.StartNew(...)
при работе с кодом, связанным с потоком пользовательского интерфейса, потому что в первый раз он проверит TaskScheduler
, обнаружит, что его нет, и будет использовать пул потоков TaskScheduler
.Когда поток завершает вычисления, используя, скажем, функцию, скажем, Compute(3)
, он возвращается, но здесь он маршалирует обратно в поток пользовательского интерфейса, а затем обновит поток пользовательского интерфейса с результатами до сих пор.
Второй ив третий раз он вызывает Compute, поскольку SynchronizationContext был перенаправлен обратно в пользовательский интерфейс, затем он будет выполняться в потоке пользовательского интерфейса, таким образом блокирует ваш пользовательский интерфейс.
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.
}
Чтобы избежать этого потенциалавместо этого используйте Task.Run(() => A());
Для получения более подробной информации, пожалуйста, смотрите статью Стивена Клири в https://blog.stephencleary.com/2013/08/startnew-is-dangerous.html и / или В чем разница между Task.Run () и Task.Factory.StartNew ()
Вот способ, который я нашел, чтобы загрузить данные для сетки данных в фоновом режиме, не блокируя ваш пользовательский интерфейс.
Сначала создайте объект блокировки и включите синхронизацию коллекции, затем фактически загрузите данные в фоновый поток с помощью Task.Run ():
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());
}
);
}
Затем в своем хранилище вы можете написать:
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}"
... >
Для получения подробной информации, пожалуйста, смотрите: