Подтвердить что ты не робот

Как сделать паузу в процедуре, а затем вернуть значение после нее?

Я работаю над проектом С#, хочу сделать небольшую паузу около 2 секунд внутри процедуры.

На самом деле я попытался использовать Invoke, но, как вы знаете, мы не можем использовать его внутри класса для этой процедуры.

Вот мой код для более подробной информации:

public class GenerateFile
{

    public CSPFEnumration.ProcedureResult GenerateFaxFile(string Daftar_No, string Channelno, string NationalCode)
    {
        string script = string.Format(" DECLARE @RC INT " +
                                        " DECLARE @Daftar_No INT = '{0}' " +
                                        " DECLARE @hokm_type_code INT = 100 " +
                                        " DECLARE @Channelno INT = '{1}' " +
                                        " DECLARE @Id_No BIGINT = '{2}' " +
                                        " EXEC @rc = [dbo].[Hokm_with_type] @Daftar_No, @hokm_type_code, @Channelno, @Id_No ",
                                        Daftar_No,
                                        Channelno,
                                        NationalCode);
        try
        {
            IEnumerable<string> commandStrings = Regex.Split(script, @"^\s*GO\s*$",
                                                    RegexOptions.Multiline | RegexOptions.IgnoreCase);
            Connect();
            foreach (string commandString in commandStrings)
            {
                if (commandString.Trim() != "")
                {
                    using (var command = new SqlCommand(commandString, Connection))
                    {
                        command.ExecuteNonQuery();
                    }
                }
            }

            DisConnect();

            string FaxFilePath = InternalConstant.FaxFilePath + "\\" + string.Format("Lhokm{0}.tif", Channelno);


            // I want to make a pause in here without locking UI

            if (File.Exists(FaxFilePath))
                return CSPFEnumration.ProcedureResult.Success;
            else
                return CSPFEnumration.ProcedureResult.Error;

        }
        catch (Exception ex)
        {
            InternalDatabase.GetInstance.InsertToPensionOrganizationException(ex);
            return CSPFEnumration.ProcedureResult.Error;
        }
    }
}

Я тоже попробовал, но я не могу вернуть правильное значение. потому что в этой процедуре, если я буду использовать await, значение вернется до окончания ожидания.

Edit:

А также я не хочу использовать Thread.Sleep, потому что он заблокирует пользовательский интерфейс. Спасибо за любую помощь.

4b9b3361

Ответ 1

Использовать функцию ожидания async:

Отметьте свой метод как асинхронный.

Добавьте Task.Delay(2000) в ожидаемую задачу.

 public async CSPFEnumration.ProcedureResult GenerateFaxFile(string Daftar_No, string Channelno, string NationalCode)
        {
                -----
                // I want to make a pause in here without locking UI
              await Task.Delay(2000);
                -----
        }

Ответ 2

Запрос на downvotes:

DoEvents

Предупреждение: полное, полное и непростительное форматирование Barnyard:

// before call (disable the UI element that called this so it can't re-enter)
DateTime st = DateTime.Now();
while(DateTime.Now.Subtract(st).TotalSeconds<3)
    System.Windows.Forms.DoEvents();
// after call (re-enable UI element)

Это будет работать. Нет ответственности, если люди указывают и смеются.

Эй, ты спросил!

Ответ 3

Вы можете посмотреть вокруг Task.Delay(), он не будет блокировать текущий поток и продолжить выполнение после количества миллисекунд.

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

Stopwatch sw = Stopwatch.StartNew();
var delay = Task.Delay(1000).ContinueWith(_ =>
                           { sw.Stop();
                             return sw.ElapsedMilliseconds; } );

Console.WriteLine("Elapsed milliseconds: {0}", delay.Result);
// The example displays output like the following:
//        Elapsed milliseconds: 1013

Или, может быть, посмотрите Timer класс.

Ответ 4

Я вижу, что он работает с событиями или задачами (если вы не можете использовать async/wait). Вот как создать событие. Мы можем использовать отдельный Thread, чтобы проверить, создан ли файл и событие пожара, если оно:

public class FileGenEventArgs : EventArgs
{
    public string ProcedureResult { get; set; }
}

public class GenerateFile
{
    public event EventHandler<FileGenEventArgs > fileCreated;

    public GenerateFile()
    {
        // subscribe for this event somewhere in your code.
        fileCreated += GenerateFile_fileCreated;
    }

    void GenerateFile_fileCreated(object sender, FileGenEventArgs args)
    {            
        // .. do something with  args.ProcedureResult
    }

    private void FileCheck()
    {
        Thread.Sleep(2000); // delay

        fileCreated(this, new FileGenEventArgs()
        {
            ProcedureResult = File.Exists(FaxFilePath) ?
              CSPFEnumration.ProcedureResult.Success :
                 CSPFEnumration.ProcedureResult.Error
        });            
    }

    public void GenerateFaxFile(string Daftar_No, string Channelno, string NationalCode)
    {
        try
        {
            // this .Sleep() represents your sql operation so change it
            Thread.Sleep(1000);

            new Thread(FileCheck).Start();
        }
        catch (Exception ex)
        {
            InternalDatabase.GetInstance.InsertToPensionOrganizationException(ex);  
        }
    }
}

Плюсы:

  • Пауза, которую вы хотели
  • Не блокирует поток пользовательского интерфейса.
  • Событийный подход (который является правильным способом решения таких проблем)

Минусы:

  • Требуется рефакторинг кода

Ответ 5

Самое простое ожидание, сохраняя отзывчивость пользовательского интерфейса, - это использовать async-wait.

Чтобы сделать это, вы должны объявить свою функцию async и вернуть задачу вместо void и Task <TResult > вместо TResult:

public async Task<CSPFEnumration.ProcedureResult> GenerateFaxFile(
    string Daftar_No,
    string Channelno,
    string NationalCode)
{
    // do your stuff,
}

Теперь, когда вы делаете что-то, что занимает некоторое время, используйте асинхронную версию функции для запуска процесса. Пока этот процесс запущен, вы можете делать другие вещи. Когда вам нужен результат, ожидайте выполнения задачи, и вы получите пустоту, если async вернет задачу или TResult, если async возвращает Task <TResult >

public async Task<CSPFEnumration.ProcedureResult> GenerateFaxFile(
    string Daftar_No,
    string Channelno,
    string NationalCode)
{
    IEnumerable<string> commandStrings = Regex.Split(
        script, @"^\s*GO\s*$", RegexOptions.Multiline | RegexOptions.IgnoreCase);

    Connect();
    foreach (var commandString in commandStrings)
     {
        if (commandString.Trim() != "")
        {
            using (var command = new SqlCommand(commandString, Connection))
                {
                    Task<int> task = command.ExecuteNonQueryAsync();
                    // while the command is being executed
                    // you can do other things.
                    // when you need the result: await
                    int result = await task;
                    // if useful: interpret result;
                }
            }
        }

        DisConnect();
        ... etc.
}
  • Каждая функция, вызывающая асинхронную функцию, должна быть объявлена ​​как async
  • каждая функция async возвращает Задачу вместо void и Задачу <TResult > вместо TResult
    • Есть только одно исключение: обработчик события может возвращать void.

Пример обработчика событий async:

private async void OnButton1_Clicked(object sender, ...)
{
    var task = GenerateFaxFile(...);
    // while the fax file is generated do some other stuff
    // when you need the result:
    var procedureResult = await task;
    Process(procedureResult);
}

Обратите внимание, что все обрабатывается потоком пользовательского интерфейса. Единственное различие заключается в том, что, как только происходит какое-то время, процесс не имеет ожиданного ожидания, но обрабатывает ввод пользовательского интерфейса.

Вышеописанное достаточно, чтобы ваш пользовательский интерфейс был отзывчивым. Вы сказали, что хотите знать, как подождать некоторое время. Из остальной части вашего вопроса я понимаю, что вы имели в виду: как прерывать процедуру, пока она чего-то ждет, поэтому пользовательский интерфейс может делать что-то другое. Если вам действительно нужно подождать некоторое время, сохраняя отзывчивость пользовательского интерфейса, используйте Task.Delay(TimeSpan).

Эрик Липперт (thanx Eric!) объяснил async-wait следующим образом fooobar.com/questions/416792/...

Предположим, что на завтрак вам придется жарить хлеб и варить яйца. Для этого есть несколько сценариев:

  • Начните поджаривать хлеб. Подождите, пока он не будет завершен. Начните готовить яйца, дождитесь окончания. Синхронная обработка. Пока вы ждёте хлеб для тоста, вы ничего не можете сделать.
  • Начните поджаривать хлеб, а хлеб поджаривается, начинайте готовить яйца. когда яйца готовятся, подождите, пока хлеб не закончит поджаривание. Это называется асинхронным, но не параллельным. Это делается с помощью основного потока, и пока этот поток что-то делает, основной поток не может ничего сделать. Но пока он ждет, у него есть время, чтобы делать другие вещи (например, сделать чай).
  • Нанимайте поваров, чтобы поджарить хлеб и варить яйца. Подождите, пока оба не закончатся. Асинхронный и параллельный: работа выполняется разными потоками. Это самое дорогое, потому что вам нужно начинать новые потоки.

Наконец, заметка об обработке исключений

Вы заметили, что если возникает исключение, вы не отключите?. Правильный способ убедиться, что размыкание всегда вызывается, следующее:

try
{
    Connect();
    ... do other stuff
}
catch (Exception exc)
{
     ... process exception
}
finally
{
    Disconnect();
}

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

Ответ 6

Вы можете использовать простой пул потоков для архивирования. Однако ваше возвращение должно выполняться асинхронно, поэтому оно не блокирует gui.

public void GenerateFaxFile(string Daftar_No, string Channelno,
            string NationalCode, Action<CSPFEnumration.ProcedureResult> result)
        {
            ThreadPool.QueueUserWorkItem(o =>
            {
                string script = "your script";
                try
                {
                    // more of your script

                    // I want to make a pause in here without locking UI
                    while (true)
                    {
                        // do your check here to unpause
                        if (stopMe == true)
                        {
                            break;
                        }

                        Thread.Sleep(500);
                    }


                    if (File.Exists(FaxFilePath))
                    {
                        result(CSPFEnumration.ProcedureResult.Success);
                        return;
                    }
                    else
                    {
                        result(CSPFEnumration.ProcedureResult.Error);
                    }


                }
                catch (Exception ex)
                {
                    InternalDatabase.GetInstance.InsertToPensionOrganizationException(ex);
                    result(CSPFEnumration.ProcedureResult.Error);
                    return;
                }

            });

        }

        public void HowToUseMe()
        {
            GenerateFaxFile("", "", "", result => {

                if (result == CSPFEnumration.ProcedureResult.Error)
                {
                    // no good
                }
                else
                {
                    // bonus time
                }
            });
        }

Ответ 7

Вы должны использовать старый хороший фоновый поток (см. ответ, написанный FabJan), или вы можете использовать async и ждать с контекстом синхронизации:

public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();
    }

    private async void buttonStart_Click(object sender, EventArgs e)
    {
        await progressBar1.DoProgress(2000);
        Trace.WriteLine("Done");          
        MessageBox.Show("Done");
    }

    private void buttonMoveButton1_Click(object sender, EventArgs e)
    {
        //to prove UI click several times buttonMove while the task is ruunning
        buttonStart.Top += 10;
    }
}

public static class WaitExtensions
{
    public static async Task DoProgress(this ProgressBar progressBar, int sleepTimeMiliseconds)
    {
        int sleepInterval = 50;
        int progressSteps = sleepTimeMiliseconds / sleepInterval; //every 50ms feedback
        progressBar.Maximum = progressSteps;

        SynchronizationContext synchronizationContext = SynchronizationContext.Current;
        await Task.Run(() =>
        {
            synchronizationContext.OperationStarted();

            for (int i = 0; i <= progressSteps; i++)
            {
                Thread.Sleep(sleepInterval);
                synchronizationContext.Post(new SendOrPostCallback(o =>
                {
                    Trace.WriteLine((int)o + "%");

                    progressBar.Value = (int)o;
                }), i);
            }

            synchronizationContext.OperationCompleted();
        });
    }
}    

Может показаться, что MessageBox done показывается до того, как ProgressBar находится на максимуме. Я обвиняю эту волшебную анимацию progressBar в Windows 8. Пожалуйста, поправьте меня, если я ошибаюсь.