FromCurrentSynchronizationContext, я что-то упускаю?
-
12-12-2019 - |
Вопрос
В настоящее время я работаю с приложением, которое считывает данные из большого двоичного файла, который содержит несколько тысяч файлов, каждый файл обрабатывается каким-либо другим классом в приложении.Этот класс возвращает либо object, либо null.Я хочу показать прогресс в основной форме, но по какой-то причине я не могу взять это в толк.
int TotalFound = 0;
var uiScheduler = TaskScheduler.FromCurrentSynchronizationContext;
BufferBlock<file> buffer = new BufferBlock<file>();
DataflowBlockOptions options = new DataflowBlockOptions(){ TaskScheduler = uiScheduler, };
var producer = new ActionBlock<someObject>(largeFile=>{
var file = GetFileFromLargeFile(largeFile);
if(file !=null){
TotalFound++;
buffer.post(file);
lblProgress.Text = String.Format("{0}", TotalFound);
}
}, options);
Приведенный выше код замораживает мою форму, даже если я использую "TaskScheduler.FromCurrentSynchronizationContext", почему?Потому что, когда я использую приведенный ниже код, моя форма обновляется нормально
DataflowBlockOptions options = new DataflowBlockOptions(){ TaskScheduler = uiScheduler, };
var producer = new ActionBlock<someObject>(largeFile=>{
var file = GetFileFromLargeFile(largeFile);
if(file !=null){
Task.Factory.StartNew(() => {
TotalFound++;
buffer.Post(file);
}).ContinueWith(uiTask => {
lblProgress.Text = String.Format("{0}", TotalFound);
},CancellationToken.None, TaskContinuationOptions.None, uiScheduler);
}
});
Я новичок во всем этом потоке данных TPL, поэтому я надеюсь, что кто-нибудь сможет пролить свет на то, почему во втором фрагменте кода это работает, а в первом фрагменте - нет.
С наилучшими пожеланиями, Мартин
Решение
Причина, по которой ваш пользовательский интерфейс заблокирован, заключается в том, что потому что ты используешь FromCurrentSynchronizationContext
.Это приводит к запуску кода в потоке пользовательского интерфейса, что означает, что он зависнет, если вы выполняете какое-то длительное действие (скорее всего GetFileFromLargeFile()
).
С другой стороны, вы должны запустить lblProgress.Text
в потоке пользовательского интерфейса.
Я не уверен, что вам следует устанавливать lblProgress.Text
непосредственно в этом коде мне это кажется слишком тесной связью.Но если вы хотите это сделать, я думаю, вам следует запустить только эту строку в потоке пользовательского интерфейса:
var producer = new ActionBlock<someObject>(async largeFile =>
{
var file = GetFileFromLargeFile(largeFile);
if (file != null)
{
TotalFound++;
await buffer.SendAsync(file);
await Task.Factory.StartNew(
() => lblProgress.Text = String.Format("{0}", TotalFound),
CancellationToken.None, TaskCreationOptions.None, uiScheduler);
}
});
Но еще лучшим решением было бы, если бы вы сделали GetFileFromLargeFile()
асинхронный и убедился, что он не выполняет никаких длительных действий в потоке пользовательского интерфейса (ConfigureAwait(false)
могу помочь вам в этом).Если вы это сделали, то код ActionBlock
можно было бы запустить в потоке пользовательского интерфейса, не замораживая ваш пользовательский интерфейс:
var producer = new ActionBlock<someObject>(async largeFile =>
{
var file = await GetFileFromLargeFile(largeFile);
if (file != null)
{
TotalFound++;
await buffer.SendAsync(file);
lblProgress.Text = String.Format("{0}", TotalFound)
}
}, options);