13

我正在做一个 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,因为它会锁定 UI。感谢您的帮助。

4

7 回答 7

7

使用异步等待功能:

将您的方法标记为 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);
                -----
        }
于 2015-10-13T06:16:33.003 回答
2

要求否决票:

做事件

警告:完全、完整和不可原谅的公然稗编程:

// 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)

似乎有效。如果人们指指点点笑,则没有责任。

嘿,你问!

于 2015-10-13T20:37:39.263 回答
1

您可以环顾四周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

或者也许看看Timer类。

于 2015-10-08T05:37:04.423 回答
0

您可以使用简单的线程池来归档它。但是,您的返回必须异步执行,因此它不会锁定 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
                }
            });
        }
于 2015-10-19T20:33:51.160 回答
0

在保持 UI 响应的同时最容易等待的是使用 async-await。

为此,您必须声明您的函数 async,并返回 Task 而不是 void 和 Task <TResult> 而不是 TResult:

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

现在,每当您执行需要一些时间的操作时,请使用该函数的异步版本来启动该过程。在此过程运行时,您可以执行其他操作。当您需要等待任务的结果时,如果异步返回任务,您将获得 void,或者如果异步返回任务,您将获得 TResult <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);
}

请注意,所有内容都由 UI 线程处理。唯一的区别是,一旦发生任何耗时的事情,进程就不会等待,而是处理 UI 输入。

以上内容足以让您的 UI 保持响应。你说你想知道如何等待一段时间。从你的问题的其余部分我明白你的意思是:如何在等待某事时中断程序,以便 UI 可以做其他事情。如果您确实需要等待一段时间以保持 UI 响应,请使用 Task.Delay(TimeSpan)。

Eric Lippert (thanx Eric!) 在 Stackoverflow 中解释了 async-await - async/await - 这种理解正确吗?

假设早餐你必须烤面包和煮鸡蛋。它有几种情况:

  1. 开始烤面包。等到它完成。开始煮鸡蛋,等到它完成。同步处理。当您等待面包烤好时,您无能为力。
  2. 开始烤面包,在烤面包的同时开始煮鸡蛋。当鸡蛋煮熟时,等到面包烤完。这称为异步,但不是并发的。它是由主线程完成的,只要这个线程做某事,主线程就不能做任何其他事情。但是在它等待的时候它有时间做其他事情(例如泡茶)
  3. 聘请厨师烤面包和煮鸡蛋。等两个都完成。异步和并发:工作由不同的线程完成。这是最昂贵的,因为您必须启动新线程。

最后是关于您的异常处理的说明

您是否注意到如果发生异常您不会断开连接?确保始终调用断开连接的正确方法如下:

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

finally 部分总是被执行,无论是否抛出任何异常。

于 2015-10-13T08:22:29.493 回答
0

我可以看到它与事件或任务一起工作(如果你不能使用异步/等待)。这是创建事件的方法。我们可以使用 separateThread来检查文件是否已创建,如果是则触发事件:

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);  
        }
    }
}

优点:

  • 你想要的暂停
  • 不会阻塞 UI 线程。
  • 基于事件的方法(这是处理此类问题的正确方法)

缺点:

  • 需要重构你的代码
于 2015-10-13T07:43:36.067 回答
0

您应该使用旧的良好后台线程(请参阅 FabJan 编写的答案),或者您可以使用async 和 await 与同步上下文

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();
        });
    }
}    

在 ProgressBar 达到最大值之前,MessageBox 可能会显示已完成。我责怪Windows 8中progressBar的这个神奇动画。如果我错了,请纠正我。

于 2015-10-19T20:40:17.083 回答