Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/269.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 异步操作已取消,但仍需要时间更新网格_C#_Async Await - Fatal编程技术网

C# 异步操作已取消,但仍需要时间更新网格

C# 异步操作已取消,但仍需要时间更新网格,c#,async-await,C#,Async Await,我在让异步操作正常工作时遇到了一点问题(对于异步来说是新的)。我的目标是有一个“加载数据”按钮,从数据库中检索一些数据并填充网格。对于某些用户来说,数据库可能比较远,此操作可能需要一些时间。考虑到这一点,我希望用户能够选择取消并选择检索较小的数据集 我让它主要与当前流程一起工作: 用户单击“加载数据…”按钮 按钮更改为“取消”并 检索数据的异步操作启动 数据被检索并保存 网格已填充 这一切都很好,但如果用户单击“取消”,则仍需要与获取网格的所有数据所需的时间相同的时间来清空网格。这让我相信长期运

我在让异步操作正常工作时遇到了一点问题(对于异步来说是新的)。我的目标是有一个“加载数据”按钮,从数据库中检索一些数据并填充网格。对于某些用户来说,数据库可能比较远,此操作可能需要一些时间。考虑到这一点,我希望用户能够选择取消并选择检索较小的数据集

我让它主要与当前流程一起工作:

  • 用户单击“加载数据…”按钮
  • 按钮更改为“取消”并 检索数据的异步操作启动
  • 数据被检索并保存 网格已填充
  • 这一切都很好,但如果用户单击“取消”,则仍需要与获取网格的所有数据所需的时间相同的时间来清空网格。这让我相信长期运行的操作实际上并没有被取消。。。但是,当我在“FindForLocationAsync”方法中调试时,如果用户请求取消,取消令牌确实会停止迭代操作并提前从方法返回

    我已经在这方面尽可能多地阅读了一段时间,但是,我现在有点陷入了僵局。任何帮助都将不胜感激

    取消令牌源

    CancellationTokenSource cancellationTokenSource = null;
    
    按钮点击法

    private async void btnSearch_Click(object sender, EventArgs e)
    {
        gridLog.DataSource = null;
        Cursor = Cursors.WaitCursor;
    
        if (btnSearch.Text.ToLower().Contains("load"))
        {
            btnSearch.Text = "Cancel";
            btnSearch.ForeColor = Color.White;
            btnSearch.BackColor = Color.Red;
    
            //get params to pass
            /* snip */
    
            cancellationTokenSource = new CancellationTokenSource();
            await Task.Run(() =>
                {
                    var ds = DocLog.FindForLocationAsync(docType, subType, days, currLocation.ID, cancellationTokenSource.Token).Result;
                    gridLog.DataSource = ds;
                });
    
            btnSearch.Text = "Load Data...";
            btnSearch.ForeColor = Color.Black;
            btnSearch.BackColor = Color.FromArgb(225, 225, 225);
        }
        else
        {
            cancelSearch();
            btnSearch.Text = "Load Data...";
            btnSearch.ForeColor = Color.Black;
            btnSearch.BackColor = Color.FromArgb(225, 225, 225);
        }
    
        Cursor = Cursors.Default;
    }
    
    取消方法

    private void cancelSearch()
    {
        if (cancellationTokenSource != null) cancellationTokenSource.Cancel();
    }
    
    长期运行法

    public async static Task<BindingList<DocLog>> FindForLocationAsync(string DocType, string SubType, int? LastXDays, Guid LocationID, CancellationToken CancellationToken)
    {
        BindingList<DocLog> dll = new BindingList<DocLog>();
    
        using (SqlConnection sqlConnection = new SqlConnection(Helper.GetConnectionString()))
        {
            sqlConnection.Open();
            using (SqlCommand sqlCommand = new SqlCommand((LastXDays == null) ? "DocLogGetAllForLocation" : "DocLogGetAllForLocationLastXDays", sqlConnection))
            {
                sqlCommand.CommandType = System.Data.CommandType.StoredProcedure;
                sqlCommand.Parameters.Add("@DocType", SqlDbType.NVarChar, 30).Value = DocType.Trim();
                sqlCommand.Parameters.Add("@SubType", SqlDbType.NVarChar, 30).Value = SubType.Trim();
                sqlCommand.Parameters.Add("@LocationID", SqlDbType.UniqueIdentifier).Value = LocationID;
                if (LastXDays != null) { sqlCommand.Parameters.Add("@NumberOfDays", SqlDbType.Int).Value = LastXDays; }
    
                SqlDataReader sqlDataReader = sqlCommand.ExecuteReader();
    
                await Task.Run(() =>
                {
                    while (sqlDataReader.Read())
                    {
                        if (CancellationToken.IsCancellationRequested)
                        {
                            dll = new BindingList<DocLog>();
                            break;
                        }
                        else
                        {
                            DocLog dl = readData(sqlDataReader);
                            dll.Add(dl);
                        }
                    }
                });
            }
        }
    
        return dll;
    }
    
    公共异步静态任务FindForLocationAsync(字符串DocType、字符串子类型、int?LastXDays、Guid LocationID、CancellationToken CancellationToken)
    {
    BindingList dll=新建BindingList();
    使用(SqlConnection SqlConnection=newsqlconnection(Helper.GetConnectionString()))
    {
    sqlConnection.Open();
    使用(SqlCommand SqlCommand=newsqlcommand((LastXDays==null)?“DocLogGetAllForLocation”:“DocLogGetAllForLocationLastXDays”,sqlConnection))
    {
    sqlCommand.CommandType=System.Data.CommandType.StoredProcess;
    sqlCommand.Parameters.Add(“@DocType”,SqlDbType.NVarChar,30).Value=DocType.Trim();
    sqlCommand.Parameters.Add(“@SubType”,SqlDbType.NVarChar,30).Value=SubType.Trim();
    sqlCommand.Parameters.Add(“@LocationID”,SqlDbType.UniqueIdentifier).Value=LocationID;
    if(LastXDays!=null){sqlCommand.Parameters.Add(“@NumberOfDays”,SqlDbType.Int).Value=LastXDays;}
    SqlDataReader SqlDataReader=sqlCommand.ExecuteReader();
    等待任务。运行(()=>
    {
    while(sqlDataReader.Read())
    {
    if(CancellationToken.IsCancellationRequested)
    {
    dll=新绑定列表();
    打破
    }
    其他的
    {
    DocLog dl=readData(sqlDataReader);
    添加(dl);
    }
    }
    });
    }
    }
    返回dll;
    }
    
    这是您修改为C语言的代码#惯用
    异步

    注意以下几点:

    • 异步代码通常指涉及异步IO的操作,其中完成信号(以及随后的完成回调)基本上是由硬件中断和操作系统产生的-不应将其与并发性(即多线程)混淆即使在另一个线程上运行的代码也可以在概念上建模为
      任务
      (实际上,
      任务
      用于多线程(
      Task.Run
      )和异步IO)。
      • 无论如何,要点是:如果您使用的是
        async
        -IO API(例如
        SqlDataReader
        FileStream
        NetworkStream
        ,等等),那么您可能不想使用
        任务。运行
    • 必须在UI线程中运行的代码之外(即WinForms和WPF UI代码),以允许在可用的后台线程中调用完成回调,这意味着UI线程不会被迫运行后台代码。
      • C语言设计师意识到垃圾
        .ConfigureAwait(false)
        的可怕的人体工程学
    • 一般来说,永远不要使用
      Task.Result
      Task.Wait()
      ,因为它们会阻塞线程并引入死锁风险(因为无法在阻塞的线程上运行连续回调)。仅在验证任务已完成后使用
      Task.Result
      (或只需执行
      wait Task
    • 您应该将
      CancellationToken
      传递给您调用的每个子
      Async
      方法
    其他挑剔之处:

    • 创建
      SqlCommand
      后,您可以在同一缩进级别上使用()
      语句组合
      ,并调用
      SqlConnection.OpenAsync
    • 参数应该是
      camelCase
      而不是
      PascalCase
    • 对实例成员(字段、方法、属性等)的引用应以
      this.
      作为前缀,以便从视觉上区分它们与本地标识符
    • 执行
      if(this.x!=null)this.x.Foo()
      不是完全安全的,因为在多线程程序中,
      x
      可以被
      if
      .Foo()
      调用之间的另一个值替换。相反,请使用
      ?。
      操作符,该操作符保留一个本地引用,以防止地毯从下方被拉出(其工作原理如下:
      X lx=this.X;if(lx!=null)lx.Foo()
      ,保证线程安全)
    • BindingList
      是(可以说)一个UI组件,不应该从概念上的“后台”函数(如
      FindForLocationSync
      方法)返回,因此我返回一个
      List
      ,然后UI将
      List
      包装在
      BindingList
    代码:

    private async void btnSearch\u单击(对象
    
    private async void btnSearch_Click(object sender, EventArgs e)
    {
        this.gridLog.DataSource = null;
        this.Cursor = Cursors.WaitCursor;
    
        if (this.btnSearch.Text.ToLower().Contains("load"))
        {
            this.btnSearch.Text = "Cancel";
            this.btnSearch.ForeColor = Color.White;
            this.btnSearch.BackColor = Color.Red;
    
            //get params to pass
            /* snip */
    
            this.cancellationTokenSource = new CancellationTokenSource();
    
            List<DocLog> list = await DocLog.FindForLocationAsync(docType, subType, days, currLocation.ID, cancellationTokenSource.Token);
            gridLog.DataSource = new BindingList<DocLog>( list );
    
            this.btnSearch.Text = "Load Data...";
            this.btnSearch.ForeColor = Color.Black;
            this.btnSearch.BackColor = Color.FromArgb(225, 225, 225);
        }
        else
        {
            CancelSearch();
            this.btnSearch.Text = "Load Data...";
            this.btnSearch.ForeColor = Color.Black;
            this.btnSearch.BackColor = Color.FromArgb(225, 225, 225);
        }
    
        this.Cursor = Cursors.Default;
    }
    
    private void CancelSearch()
    {
        this.cancellationTokenSource?.Cancel();
    }
    
    public async static Task<List<DocLog>> FindForLocationAsync(string DocType, string SubType, int? LastXDays, Guid LocationID, CancellationToken cancellationToken)
    {
        List<DocLog> dll = new List<DocLog>();
    
        using (SqlConnection sqlConnection = new SqlConnection(Helper.GetConnectionString()))
        using (SqlCommand sqlCommand = sqlConnection.CreateCommand())
        {
            await sqlConnection.OpenAsync(cancellationToken).ConfigureAwait(false);
    
            sqlCommand.CommandText = (LastXDays == null) ? "DocLogGetAllForLocation" : "DocLogGetAllForLocationLastXDays";
            sqlCommand.CommandType = System.Data.CommandType.StoredProcedure;
            sqlCommand.Parameters.Add("@DocType", SqlDbType.NVarChar, 30).Value = DocType.Trim();
            sqlCommand.Parameters.Add("@SubType", SqlDbType.NVarChar, 30).Value = SubType.Trim();
            sqlCommand.Parameters.Add("@LocationID", SqlDbType.UniqueIdentifier).Value = LocationID;
            if (LastXDays != null) { sqlCommand.Parameters.Add("@NumberOfDays", SqlDbType.Int).Value = LastXDays; }
    
            using( SqlDataReader sqlDataReader = await sqlCommand.ExecuteReaderAsync(cancellationToken).ConfigureAwait(false) )
            {
                while (await sqlDataReader.ReadAsync(cancellationToken).ConfigureAwait(false))
                {
                    if (cancellationToken.IsCancellationRequested) break;
    
                    DocLog dl = readData(sqlDataReader);
                    dll.Add(dl);
                }
            }
        }
    
        return dll;
    }