执行异步操作

某些数据库操作(例如命令执行)可能要花费很长时间才能完成。在此类情况下,单线程应用程序必须阻塞其他操作,并且等待该命令完成,然后才可以继续执行它们自己的操作。比较而言,如果能够将长时间运行的操作分配给某个后台线程,就可以允许前台线程在整个操作期间保持活动状态。例如,在 Windows 应用程序中,通过将长时间运行的操作委托给后台线程,可允许用户界面线程在操作执行时保持响应状态。

.NET Framework 提供若干标准异步设计模式,开发人员可以通过这些模式充分利用后台线程并释放用户界面线程或高优先级的线程以完成其他操作。ADO.NET 在其 SqlCommand 类中支持相同的设计模式。具体而言,BeginExecuteNonQueryBeginExecuteReaderBeginExecuteXmlReader 方法(与 EndExecuteNonQueryEndExecuteReaderEndExecuteXmlReader 方法搭配)提供异步支持。

使用等待句柄创建 ASP.NET 应用程序

在您的应用程序一次只处理一个异步操作时,用于处理异步操作的回调和轮询模型十分有用。等待模型提供了一种更灵活的方式来处理多个异步操作。有两种等待模型,是根据用于实现它们的 WaitHandle 方法命名的:等待(任何)模型和等待(所有)模型。

要使用上述任一等待模型,您需要使用 BeginExecuteNonQueryBeginExecuteReaderBeginExecuteXmlReader 方法返回的 IAsyncResult 对象的 AsyncWaitHandle 属性。WaitAnyWaitAll 方法都要求您将多个 WaitHandle 对象一起组合在一个数组中,作为一个参数发送。

这两种等待方法都监控异步操作,等待操作完成。WaitAny 方法等待任何操作完成或超时。一旦您知道某一特定操作完成后,就可以处理其结果,然后继续等待下一个操作完成或超时。WaitAll 方法等待 WaitHandle 实例数组中的所有进程都完成或超时后,再继续。

当您需要在不同服务器上运行时间长度不等的多个操作时,或者在服务器的性能强大到足以同时处理所有查询时,等待模型的优点可以最大地发挥。在此处提供的示例中,通过将长度不等的多个 WAITFOR 命令添加到不重要的 SELECT 查询,三个查询模拟长进程。

 

下面的示例说明等待(任何)模型。一旦启动了三个异步进程后,就调用 WaitAny 方法以等待其中任何一个进程完成。在每个进程完成后,调用 EndExecuteReader 方法并读取结果 SqlDataReader 对象。在此时,实际的应用程序将很可能使用 SqlDataReader 填充该页的某个部分。在这个简单的示例中,进程完成时间被添加到与该进程相对应的文本框中。合起来看,文本框中的这些时间说明以下结论:每次进程完成后都执行代码。

要设置此示例,请创建一个新的 ASP.NET 网站项目。将一个 Button 控件和四个 TextBox 控件放置于页面上(接受每个控件的默认名称)。

在窗体的类中添加以下代码,根据环境的需要修改连接字符串。

[C#]

// Add the following using statements, if they are not already there.
using System;
using System.Data;
using System.Configuration;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;
using System.Threading;
using System.Data.SqlClient;

// Add this code to the page's class
string GetConnectionString()
     //  To avoid storing the connection string in your code,            
     //  you can retrieve it from a configuration file. 
     //  If you have not included "Asynchronous Processing=true" 
     //  in the connection string, the command will not be able
     //  to execute asynchronously.
{
     return "Data Source=(local);Integrated Security=SSPI;" +
          "Initial Catalog=AdventureWorks;" +
          "Asynchronous Processing=true";
}
void Button1_Click(object sender, System.EventArgs e)
{
     //  In a real-world application, you might be connecting to 
     //   three different servers or databases. For the example,
     //   we connect to only one.
     
     SqlConnection connection1 = 
          new SqlConnection(GetConnectionString());
     SqlConnection connection2 = 
          new SqlConnection(GetConnectionString());
     SqlConnection connection3 = 
          new SqlConnection(GetConnectionString());
     //  To keep the example simple, all three asynchronous 
     //  processes select a row from the same table. WAITFOR
     //  commands are used to emulate long-running processes
     //  that complete after different periods of time.

     string commandText1 = "WAITFOR DELAY '0:0:01';" + 
          "SELECT * FROM Production.Product " + 
          "WHERE ProductNumber = 'BL-2036'";
     string commandText2 = "WAITFOR DELAY '0:0:05';" + 
          "SELECT * FROM Production.Product " + 
          "WHERE ProductNumber = 'BL-2036'";
     string commandText3 = "WAITFOR DELAY '0:0:10';" + 
          "SELECT * FROM Production.Product " + 
          "WHERE ProductNumber = 'BL-2036'";
     try
          //  For each process, open a connection and begin 
          //  execution. Use the IAsyncResult object returned by 
          //  BeginExecuteReader to add a WaitHandle for the 
          //  process to the array.
     {
          connection1.Open();
          SqlCommand command1 =
               new SqlCommand(commandText1, connection1);
          IAsyncResult result1 = command1.BeginExecuteReader();
          WaitHandle waitHandle1 = result1.AsyncWaitHandle;

          connection2.Open();
          SqlCommand command2 =
               new SqlCommand(commandText2, connection2);
          IAsyncResult result2 = command2.BeginExecuteReader();
          WaitHandle waitHandle2 = result2.AsyncWaitHandle;

          connection3.Open();
          SqlCommand command3 =
               new SqlCommand(commandText3, connection3);
          IAsyncResult result3 = command3.BeginExecuteReader();
          WaitHandle waitHandle3 = result3.AsyncWaitHandle;

          WaitHandle[] waitHandles = {
               waitHandle1, waitHandle2, waitHandle3
          };

          int index;
          for (int countWaits = 0; countWaits <= 2; countWaits++)
          {
               //  WaitAny waits for any of the processes to 
               //  complete. The return value is either the index 
               //  of the array element whose process just 
               //  completed, or the WaitTimeout value.

               index = WaitHandle.WaitAny(waitHandles, 
                    60000, false);
               //  This example doesn't actually do anything with 
               //  the data returned by the processes, but the 
               //  code opens readers for each just to demonstrate     
               //  the concept.
               //  Instead of using the returned data to fill the 
               //  controls on the page, the example adds the time
               //  the process was completed to the corresponding
               //  text box.

               switch (index)
               {
                    case 0:
                         SqlDataReader reader1;
                         reader1 = 
                              command1.EndExecuteReader(result1);
                         if (reader1.Read())
                         {
                           TextBox1.Text = 
                           "Completed " +
                           System.DateTime.Now.ToLongTimeString();
                         }
                         reader1.Close();
                         break;
                    case 1:
                         SqlDataReader reader2;
                         reader2 = 
                              command2.EndExecuteReader(result2);
                         if (reader2.Read())
                         {
                           TextBox2.Text = 
                           "Completed " +
                           System.DateTime.Now.ToLongTimeString();
                         }
                         reader2.Close();
                         break;
                    case 2:
                         SqlDataReader reader3;
                         reader3 = 
                              command3.EndExecuteReader(result3);
                         if (reader3.Read())
                         {
                           TextBox3.Text = 
                           "Completed " +
                           System.DateTime.Now.ToLongTimeString();
                         }
                         reader3.Close();
                         break;
                    case WaitHandle.WaitTimeout:
                         throw new Exception("Timeout");
                         break;
               }
          }
     }
     catch (Exception ex)
     {
          TextBox4.Text = ex.ToString();
     }
     connection1.Close();
     connection2.Close();
     connection3.Close();
}

 

`useEffect` 是 React 中的一个钩子函数,用于在组件渲染之后执行某些副作用操作。然而,`useEffect` 本身并不支持直接执行异步操作,因为它的执行环境是在组件渲染之后,此时 JavaScript 的异步执行环境已经结束了。 如果你需要在 `useEffect` 中执行异步操作,你可以使用一些其他的技术或者库来实现。一种常见的方法是使用 `useCallback` 和 `useEffect` 的组合,将异步操作封装成一个函数,然后在 `useEffect` 中调用这个函数。 下面是一个简单的示例: ```jsx import { useCallback, useEffect } from 'react'; function Example() { const asyncOperation = useCallback(() => { // 你的异步操作代码 // 这里只是一个示例,你需要替换成实际的异步操作代码 setTimeout(() => { console.log('异步操作完成'); }, 1000); // 模拟异步操作需要一段时间完成 }, []); // 空数组表示这个依赖项不会变化,所以这个回调函数不会重新被创建 useEffect(() => { asyncOperation(); // 在组件卸载时取消异步操作 }, []); // 空数组表示这个依赖项不会变化,所以这个 `useEffect` 不会在每次渲染时被调用 return <div>异步操作示例</div>; } ``` 在这个示例中,我们使用 `useCallback` 创建了一个返回异步操作的函数 `asyncOperation`。然后在 `useEffect` 中调用这个函数。这样,即使组件重新渲染,也不会重新创建 `asyncOperation` 函数,因为它的依赖项没有变化。此外,我们还通过 `useEffect` 在组件卸载时取消了异步操作,以确保它不会被错误地持续执行。 另外,React 团队也在积极推进 `React Fiber` 项目的开发,该项目的目标是实现更好的性能和异步操作支持。其中一个可能的改进是使用一个新的 "meta"(元数据)系统来跟踪组件的当前状态和生命周期阶段,这可能有助于更好地处理异步操作和性能优化。不过,这个项目的进展还不确定,所以目前我们仍然需要使用上述的方法来处理异步操作
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值