Асинхронность в C#

472
27 января 2017, 04:32

Мне нужно реализовать асинхронную работу UI и алгоритма в своей программе. Я уже делал что-то подобное в прошлом, но тогда мой алгоритм ничего не возвращал, а только работал с файлами. Примерно это выглядело так:

async void CopyFiles(DirectoryInfo dir) {
  var len = await Task.Run(() => dir.GetFiles().Length);
  var p = new Progress<int>(i => {
    progress.Text = i + " файлов из " + len + " отсортировано";
    pBar.PerformStep();
  });
  await Task.Run(() => RealCopyFiles(dir, p));
  bDone.Visible = true;
}
void RealCopyFiles(DirectoryInfo dir, IProgress<int> progress) {
  var i = 0;
  foreach (var k in dir.GetFiles()) {
    string month = k.LastWriteTime.ToString("yyyy-MM");
    //...
    i += 1;
    progress.Report(i);
  }
}

В CopyFiles я вызывал RealCopyFiles, который оповещал о прогрессе. Теперь мне нужно, что бы RealCopyFiles ещё и возвращал значение. Следовательно, в месте, где я вызываю CopyFiles мне нужно присвоить результат работы метода RealCopyFiles другой переменной. Но как бы я не пытался, компилятор всегда ругается, что нельзя такое делать с асинхронными методами(я как бы и не против, просто по другому я не знаю как это сделать). То каким образом можно реорганизовать данный код с учётом того, что RealCopyFiles, а следовательно и CopyFiles должны возвращать значение?

Добавил поточный пример:

public int[,] getCrossCorrelation(byte[,] main_Image, byte[,] template_image)
        {
            //..
                rowsCompleted++;
                progressBar1.Value = (int)(((double)rowsCompleted / height) * 100);
         //..
            return crossCorelation;
        }

С этого метода я хотел бы вынести обработку прогресс бара в отдельный поток. Но если делать это по аналогии с предыдущим примером, я не могу вернуть результат работы этого метода в асинхронный метод, который, в свою очередь, должен так же вернуть этот результат в том месте, где он вызывается

Что имею на данный момент:

public int[,] getCrossCorrelation(byte[,] main_Image, byte[,] template_image, IProgress<int> progress)
        {
/..
                rowsCompleted++;
                currentProgress = (int)(((double)rowsCompleted / height) * 100);
                progress.Report(currentProgress);
/..
            return crossCorelation;
        }
async Task<int[,]> test()
        {
            var p = new Progress<int>(currentProgress =>
            {
                progressBar1.Value = currentProgress;
            });
            var value = await Task.Run(() => getCrossCorrelation(byteImageMain, byteImageTemplate, p));
            return value;
        }

 private void btnStart_Click(object sender, EventArgs e)
        {
        /..
            int[,] crossCorelation = test(); //<-- тут ошибка
    }

Ошибка:неявное преобразование типа "System.Threading.Tasks.Task" в "int[,]" невозможно.

Answer 1

компилятор всегда ругается, что нельзя такое делать с асинхронными методами

Очень даже можно. Ну или покажите, как именно вы это делаете и что именно говорит компилятор. Вот так все работает:

int RealCopyFiles(DirectoryInfo dir, IProgress<int> progress)
{
    //...
    return 42;
}
async Task<int> CopyFiles(DirectoryInfo dir) {
    //...
    var value = await Task.Run(() => RealCopyFiles(dir, p)); // value = 42
    //...
    return value;
}

UPD

Поскольку метод теперь возвращает таск, этот метод тоже надо await'ить:

private async void btnStart_Click(object sender, EventArgs e)
{
    //..
    int[,] crossCorelation = await test(); //<-- тут ошибка
}

Поскольку все это дело происходит в обработчике события, а он не может возвращать Task, тело метода крайне желательно обернуть в try/catch. Почему -- смотрите тут.

Answer 2

Асинхронный метод должен возвращать Task, Task<Т> либо ничего не возвращать (void). Почитать можно здесь https://msdn.microsoft.com/ru-ru/library/mt674882.aspx

async Task<int> MyMethodAsync()
{ 
    return 0;
}
Answer 3

Может вам поможет документация на msdn?

// Описание асинхроного метода
async Task<MyClass> GetMyClassAsync()  
{
    MyClass myClassObject = new MyClass();
    // Do Somethisng
    return myClassObject;  
}  
// Получение результата
MyClass result = await GetMyClassAsync();  
READ ALSO
Несколько вопросов про Dapper

Несколько вопросов про Dapper

Поддерживает ли они подготовку запроса или каждый запрос компилируется по новой?

354
Переменная в C#

Переменная в C#

Учусь C#, читаю чужой код и возник вопрос, что обозначают {0} таким символом, это элемент массива? Вот весь кусок

324
Повышение производительности DataTable

Повышение производительности DataTable

За счет чего можно достигнуть высокой производительности в DataTable при поиске в ней?

362
Временная сложность алгоритма List.AddRange()

Временная сложность алгоритма List.AddRange()

Из названия понятна суть проблемыИнтересует какова временная сложность алгоритма метода AddRange() списка в C#?

277