Как сделать паузу в процедуре, а затем вернуть значение после нее?
Я работаю над проектом C#, хочу сделать небольшую паузу около 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, значение вернется до окончания await.
Редактировать:
А также я не хочу использовать Thread.Sleep
потому что это заблокирует пользовательский интерфейс. Спасибо за любую помощь.
7 ответов
Использовать функцию асинхронного ожидания:
Отметьте ваш метод как асинхронный.
Добавьте 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);
-----
}
Запрашиваемая вниз голосов:
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)
Это будет работать. Нет ответственности, если люди указывают и смеются.
Эй, ты спросил!
Вы можете осмотреться Task.Delay()
он не заблокирует текущий поток и продолжит выполнение через количество миллисекунд.
Пример использования из 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
Или, может быть, посмотреть вокруг класса Таймер.
Я вижу, как это работает с событиями или задачами (если вы не можете использовать async / await). Это как создать событие. Мы можем использовать отдельные 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);
}
}
}
Плюсы:
- Пауза, что вы хотели
- Не блокирует поток пользовательского интерфейса.
- Основанный на событиях подход (который является правильным способом решения таких проблем)
Минусы:
- Требуется рефакторинг вашего кода
Вы можете использовать простой пул потоков для архивирования. Однако ваше возвращение должно выполняться асинхронно, чтобы не блокировать графический интерфейс.
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
}
});
}
Самый простой способ подождать, пока пользовательский интерфейс реагирует на запросы, - использовать async-await.
Чтобы сделать это, вы должны объявить вашу функцию асинхронной и вернуть Task вместо void и Task<TResult
> вместо TResult:
public async Task<CSPFEnumration.ProcedureResult> GenerateFaxFile(
string Daftar_No,
string Channelno,
string NationalCode)
{
// do your stuff,
}
Теперь, когда вы делаете что-то, что занимает некоторое время, используйте асинхронную версию функции, чтобы запустить процесс. Пока этот процесс запущен, вы можете делать другие вещи. Когда вам нужен результат, дождитесь выполнения задачи и получите пустое значение, если асинхронный возврат вернет Task, или TResult, если асинхронный возврат вернет 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.
}
- Каждая функция, которая вызывает асинхронную функцию, должна быть объявлена как асинхронная
- каждая асинхронная функция возвращает Task вместо void и Task
<TResult
> вместо TResult- Есть только одно исключение: обработчик события может вернуть void.
Пример асинхронного обработчика событий:
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).
Эрик Липперт (спасибо Эрику!) Объяснил async-await следующим образом в Stackru - async/await - верно ли это понимание?
Предположим, на завтрак нужно поджарить хлеб и приготовить яйца. Для этого есть несколько сценариев:
- Начните поджаривать хлеб. Подождите, пока это не закончится. Начните готовить яйца, подождите, пока оно не закончится. Синхронная обработка. Пока вы ждете, чтобы хлеб поджарился, вы больше ничего не можете сделать.
- Начните жарить хлеб, в то время как хлеб жарится, начните готовить яйца. когда яйца приготовлены, подождите, пока хлеб не закончится. Это называется асинхронным, но не одновременным. Это делается основным потоком, и пока этот поток что-то делает, основной поток больше ничего не может делать. Но пока он ждет, у него есть время заняться другими делами (например, приготовить чай)
- Нанимайте поваров, чтобы поджарить хлеб и приготовить яйца. Подождите, пока оба не закончены. Асинхронный и одновременный: работа выполняется разными потоками. Это самый дорогой, потому что вы должны начать новые темы.
Наконец записка о вашей обработке исключений
Вы замечаете, что если происходит исключение, вы не отключаетесь? Правильный способ убедиться, что всегда вызывается разъединение:
try
{
Connect();
... do other stuff
}
catch (Exception exc)
{
... process exception
}
finally
{
Disconnect();
}
Часть finally всегда выполняется независимо от того, было ли выброшено исключение или нет.
Вам следует использовать старый добрый фоновый поток (см. Ответ, написанный 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 сделано Показывает, пока ProgressBar не достиг своего максимума. Я виню в этом волшебную анимацию progressBar в Windows 8. Пожалуйста, исправьте меня, если я ошибаюсь.