Вопрос

В настоящее время я работаю с приложением, которое считывает данные из большого двоичного файла, который содержит несколько тысяч файлов, каждый файл обрабатывается каким-либо другим классом в приложении.Этот класс возвращает либо 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);
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top