Как создать асинхронный метод - PullRequest
51 голосов
/ 21 июля 2009

У меня есть простой метод в моем приложении C #, он выбирает файл с FTP-сервера, анализирует его и сохраняет данные в БД. Я хочу, чтобы он был асинхронным, чтобы пользователь выполнял другие операции над приложением, после того, как анализ завершен, он должен получить сообщение о том, что «анализ завершен».

Я знаю, что это может быть достигнуто с помощью асинхронного вызова метода, но Я не знаю, как это сделать Кто-нибудь может мне помочь, пожалуйста?

Ответы [ 8 ]

75 голосов
/ 21 июля 2009

Вам нужно использовать делегаты и содержащийся в них метод BeginInvoke для асинхронного запуска другого метода. В конце метода, выполняемого делегатом, вы можете уведомить пользователя. Например:

class MyClass
{
    private delegate void SomeFunctionDelegate(int param1, bool param2);
    private SomeFunctionDelegate sfd;

    public MyClass()
    {
        sfd = new SomeFunctionDelegate(this.SomeFunction);
    }

    private void SomeFunction(int param1, bool param2)
    {
        // Do stuff

        // Notify user
    }

    public void GetData()
    {
        // Do stuff

        sfd.BeginInvoke(34, true, null, null);
    }
}

Читать на http://msdn.microsoft.com/en-us/library/2e08f6yc.aspx

19 голосов
/ 28 июня 2012

попробуйте этот метод

public static void RunAsynchronously(Action method, Action callback) {
    ThreadPool.QueueUserWorkItem(_ =>
    {
        try {
            method();
        } 
        catch (ThreadAbortException) { /* dont report on this */ } 
        catch (Exception ex) {
        }
        // note: this will not be called if the thread is aborted
        if (callback!= null) callback();
    });
}

Использование:

RunAsynchronously( () => { picks file from FTP server and parses it}, 
       () => { Console.WriteLine("Parsing is done"); } );
6 голосов
/ 21 июля 2009

Каждый раз, когда вы делаете что-то асинхронное, вы используете отдельный поток, либо новый поток, либо один, взятый из пула потоков. Это означает, что все, что вы делаете асинхронно, должно быть очень осторожным при взаимодействии с другими потоками.

Один из способов сделать это - поместить код для асинхронного потока (назовите его потоком "A") вместе со всеми его данными в другой класс (назовите его классом "A"). Убедитесь, что поток «A» обращается только к данным в классе «A». Если поток «A» касается только класса «A», а другой поток не касается данных класса «A», то есть еще одна проблема:

public class MainClass
{
    private sealed class AsyncClass
    {
        private int _counter;
        private readonly int _maxCount;

        public AsyncClass(int maxCount) { _maxCount = maxCount; }

        public void Run()
        {
            while (_counter++ < _maxCount) { Thread.Sleep(1); }
            CompletionTime = DateTime.Now;
        }

        public DateTime CompletionTime { get; private set; }
    }

    private AsyncClass _asyncInstance;
    public void StartAsync()
    {
        var asyncDoneTime = DateTime.MinValue;
        _asyncInstance = new AsyncClass(10);
        Action asyncAction = _asyncInstance.Run;
        asyncAction.BeginInvoke(
            ar =>
                {
                    asyncAction.EndInvoke(ar);
                    asyncDoneTime = _asyncInstance.CompletionTime;
                }, null);
        Console.WriteLine("Async task ended at {0}", asyncDoneTime);
    }
}

Обратите внимание, что единственная часть AsyncClass, которая затрагивается извне, - это ее открытый интерфейс, и единственная часть, которая является данными, - это CompletionTime. Обратите внимание, что это только , затронутый после завершения асинхронной задачи. Это означает, что ничто иное не может мешать выполнению задач внутри, и это не может мешать чему-либо еще.

4 голосов
/ 06 февраля 2012

В Asp.Net я использую множество статических методов для выполнения заданий. Если это просто работа, где мне нужно нет ответа или статус, я делаю что-то простое, как показано ниже. Как вы можете видеть, я могу выбрать либо ResizeImages, либо ResizeImagesAsync, в зависимости от того, хочу ли я ждать его завершения или нет

Объяснение кода: я использую http://imageresizing.net/ для изменения размера / обрезки изображений, а метод SaveBlobPng - для сохранения изображений в Azure (облако), но поскольку это не имеет значения для этой демонстрации, я не включил это код. Это хороший пример трудоемких задач, хотя

private delegate void ResizeImagesDelegate(string tempuri, Dictionary<string, string> versions);
private static void ResizeImagesAsync(string tempuri, Dictionary<string, string> versions)
{
    ResizeImagesDelegate worker = new ResizeImagesDelegate(ResizeImages);
    worker.BeginInvoke(tempuri, versions, deletetemp, null, null);
}
private static void ResizeImages(string tempuri, Dictionary<string, string> versions)
{
    //the job, whatever it might be
    foreach (var item in versions)
    {
        var image = ImageBuilder.Current.Build(tempuri, new ResizeSettings(item.Value));
        SaveBlobPng(image, item.Key);
        image.Dispose();
    }
}

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

private static void ResizeImagesAsync(string tempuri, Dictionary<string, string> versions)
{
    Thread t = new Thread (() => ResizeImages(tempuri, versions, null, null));
    t.Start(); 
}
4 голосов
/ 21 июля 2009

Вот две ссылки о потоках в C #

Я бы начал читать о BackgroundWorker class

1 голос
/ 21 июля 2009

ThreadPool.QueueUserWorkItem - это самый быстрый способ запустить процесс в другом потоке.

Имейте в виду, что объекты пользовательского интерфейса имеют «сходство потоков» и не могут быть доступны из любого потока, кроме того, который их создал.

Таким образом, помимо проверки ThreadPool (или использования модели асинхронного программирования через делегатов), вам необходимо проверить Диспетчеры (wpf) или InvokeRequired (winforms).

0 голосов
/ 06 января 2019

.NET получил новое ключевое слово async для асинхронных функций. Вы можете начать копать на docs.microsoft.com (async) . Кратчайший общий способ сделать функцию асхринной - это изменить функцию F :

Object F(Object args)
{
    ...
    return RESULT;
}

примерно так:

async Task<Object> FAsync(Object args)
{
    ...
    await RESULT_FROM_PROMISE;
    ...
    return RESULT;
}

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

0 голосов
/ 21 июля 2009

В конце концов вам придется использовать какую-то многопоточность. В основном это работает так, что вы запускаете функцию с новым потоком, и она будет работать до конца функции.

Если вы используете Windows Forms, то хорошая обертка, которую они имеют для этого, - это Background Worker. Он позволяет работать в фоновом режиме, не блокируя форму пользовательского интерфейса, и даже предоставляет способ связи с формами и предоставления событий обновления прогресса.

Фоновый рабочий

...