diff --git a/docs/framework/wcf/feature-details/deploying-an-internet-information-services-hosted-wcf-service.md b/docs/framework/wcf/feature-details/deploying-an-internet-information-services-hosted-wcf-service.md index 4d35d749a0e08..2669986a839f9 100644 --- a/docs/framework/wcf/feature-details/deploying-an-internet-information-services-hosted-wcf-service.md +++ b/docs/framework/wcf/feature-details/deploying-an-internet-information-services-hosted-wcf-service.md @@ -9,7 +9,7 @@ Developing and deploying a Windows Communication Foundation (WCF) service that i - Ensure that IIS, ASP.NET, WCF, and the WCF activation component are correctly installed and registered. -- Create a new IIS application, or reuse an existing [!INCLUDE[vstecasp](../../../../includes/vstecasp-md.md)] application. +- Create a new IIS application, or reuse an existing ASP.NET application. - Create a .svc file for the WCF service. @@ -17,20 +17,21 @@ Developing and deploying a Windows Communication Foundation (WCF) service that i - Configure the WCF service. - For a detailed walkthrough of creating an IIS-hosted WCF service, see [How to: Host a WCF Service in IIS](how-to-host-a-wcf-service-in-iis.md). +For a detailed walkthrough of creating an IIS-hosted WCF service, see [How to: Host a WCF Service in IIS](how-to-host-a-wcf-service-in-iis.md). ## Ensure That IIS, ASP.NET and WCF Are Correctly Installed and Registered -WCF, IIS, and ASP.NET must be installed for IIS-hosted WCF services to function correctly. The procedures for installing WCF (as part of the [!INCLUDE[vstecwinfx](../../../../includes/vstecwinfx-md.md)]), ASP.NET and IIS vary depending on the operating system version being used. For more information about installing WCF and the [!INCLUDE[vstecwinfx](../../../../includes/vstecwinfx-md.md)], see [Microsoft .NET Framework 4 Web Installer](https://go.microsoft.com/fwlink/?LinkId=201185). Instructions for installing IIS can be found at [Installing IIS](https://go.microsoft.com/fwlink/?LinkId=201188). -The installation process for the [!INCLUDE[vstecwinfx](../../../../includes/vstecwinfx-md.md)] automatically registers WCF with IIS if IIS is already present on the machine. If IIS is installed after the [!INCLUDE[vstecwinfx](../../../../includes/vstecwinfx-md.md)], an additional step is required to register WCF with IIS and [!INCLUDE[vstecasp](../../../../includes/vstecasp-md.md)]. You can do this as follows, depending on your operating system: +WCF, IIS, and ASP.NET must be installed for IIS-hosted WCF services to function correctly. The procedures for installing WCF (as part of the .NET Framework), ASP.NET, and IIS vary depending on your operating system. For more information about installing WCF and the .NET Framework, see [Install the .NET Framework for developers](../../install/guide-for-developers.md). To install IIS on Windows 10, open **Programs and Features** in **Control Panel** and then select **Turn Windows features on or off**. In **Windows Features**, select **Internet Information Services** and then choose **OK**. -- [!INCLUDE[wxpsp2](../../../../includes/wxpsp2-md.md)], Windows 7, and [!INCLUDE[ws2003](../../../../includes/ws2003-md.md)]: Use the [ServiceModel Registration Tool (ServiceModelReg.exe)](../../../../docs/framework/wcf/servicemodelreg-exe.md) tool to register WCF with IIS: To use this tool, type **ServiceModelReg.exe /i /x** in the Visual Studio command prompt. You can open this command prompt by clicking the start button, selecting **All Programs**, **Microsoft Visual Studio 2012**, **Visual Studio Tools**, and **Visual Studio Command Prompt** +![Windows Features with IIS highlighted](media/windows-features-iis.png) -- [!INCLUDE[wv](../../../../includes/wv-md.md)]: Install the Windows Communication Foundation Activation Components subcomponent of the [!INCLUDE[vstecwinfx](../../../../includes/vstecwinfx-md.md)]. To do this, in Control Panel, click **Add or Remove Programs** and then **Add\/Remove Windows Components**. This activates the **Windows Component Wizard**. +Instructions for installing IIS on other operating systems can be found at [Install IIS on Windows Vista and Windows 7](/iis/install/installing-iis-7/installing-iis-on-windows-vista-and-windows-7) and [Install IIS 8.5 on Windows Server 2012 R2](/iis/install/installing-iis-85/installing-iis-85-on-windows-server-2012-r2). -- Windows 7: +The installation process for .NET Framework automatically registers WCF with IIS if IIS is already present on the machine. If IIS is installed after the .NET Framework, an additional step is required to register WCF with IIS and ASP.NET. You can do this as follows, depending on your operating system: -Finally you must verify that ASP.NET is configured to use the .NET Framework version 4. You do this by running the ASPNET_Regiis tool with the –i option. For more information, see [ASP.NET IIS Registration Tool](https://go.microsoft.com/fwlink/?LinkId=201186) +- Windows 7 and Windows Server 2003: Use the [ServiceModel Registration Tool (ServiceModelReg.exe)](../../../../docs/framework/wcf/servicemodelreg-exe.md) tool to register WCF with IIS. To use this tool, type **ServiceModelReg.exe /i /x** in the [Developer Command Prompt for Visual Studio](../../tools/developer-command-prompt-for-vs.md). + +- Windows 7: Finally, you must verify that ASP.NET is configured to use the .NET Framework version 4 or later. You do this by running the ASPNET_Regiis tool with the `–i` option. For more information, see [ASP.NET IIS Registration Tool](https://go.microsoft.com/fwlink/?LinkId=201186). ## Create a New IIS Application or Reuse an Existing ASP.NET Application @@ -39,6 +40,7 @@ IIS-hosted WCF services must reside inside of an IIS application. You can create Note that [!INCLUDE[iis601](../../../../includes/iis601-md.md)] and later versions periodically restart an isolated object-oriented programming application. The default value is 1740 minutes. The maximum value supported is 71,582 minutes. This restart can be disabled. For more information about this property, see the [PeriodicRestartTime](https://go.microsoft.com/fwlink/?LinkId=109968). ## Create an .svc File for the WCF Service + WCF services hosted in IIS are represented as special content files (.svc files) inside the IIS application. This model is similar to the way ASMX pages are represented inside of an IIS application as .asmx files. A .svc file contains a WCF-specific processing directive ([\@ServiceHost](../../../../docs/framework/configure-apps/file-schema/wcf-directive/servicehost.md)) that allows the WCF hosting infrastructure to activate hosted services in response to incoming messages. The most common syntax for a .svc file is in the following statement. ``` @@ -53,10 +55,11 @@ new ServiceHost( typeof( MyNamespace.MyServiceImplementationTypeName ) ); Additional hosting configuration, such as creating a list of base addresses for the service can also be done. You can also use a custom to extend the directive for use with custom hosting solutions. The IIS applications that host WCF services are not responsible for managing the creation and lifetime of instances. The managed WCF hosting infrastructure creates the necessary instance dynamically when the first request is received for the .svc file. The instance is not released until either it is closed explicitly by code or when the application is recycled. - For more information about the syntax for .svc files, see [\@ServiceHost](../../../../docs/framework/configure-apps/file-schema/wcf-directive/servicehost.md). +For more information about the syntax for .svc files, see [\@ServiceHost](../../../../docs/framework/configure-apps/file-schema/wcf-directive/servicehost.md). ## Deploy the Service Implementation to the IIS Application - WCF services hosted in IIS use the same dynamic compilation model as [!INCLUDE[vstecasplong](../../../../includes/vstecasplong-md.md)]. Just as with [!INCLUDE[vstecasp](../../../../includes/vstecasp-md.md)], you can deploy the implementation code for IIS-hosted WCF services in several ways at various locations, as follows: + +WCF services hosted in IIS use the same dynamic compilation model as [!INCLUDE[vstecasplong](../../../../includes/vstecasplong-md.md)]. Just as with ASP.NET, you can deploy the implementation code for IIS-hosted WCF services in several ways at various locations, as follows: - As a precompiled .dll file located in the global assembly cache (GAC) or in the application’s \bin directory. Precompiled binaries are not updated until a new version of the class library is deployed. @@ -64,9 +67,10 @@ Additional hosting configuration, such as creating a list of base addresses for - As uncompiled code placed directly in the .svc file. Implementation code can also be located inline in the service’s .svc file, after the \@ServiceHost directive. Any changes to inline code cause the application to be recycled and recompiled when the next request is received. - For more information about the [!INCLUDE[vstecasplong](../../../../includes/vstecasplong-md.md)] compilation model, see [ASP.NET Compilation Overview](https://go.microsoft.com/fwlink/?LinkId=94773). +For more information about the [!INCLUDE[vstecasplong](../../../../includes/vstecasplong-md.md)] compilation model, see [ASP.NET Compilation Overview](https://go.microsoft.com/fwlink/?LinkId=94773). ## Configure the WCF Service + IIS-hosted WCF services store their configuration in the applications Web.config file. IIS-hosted services use the same configuration elements and syntax as WCF services hosted outside of IIS. However, the following constraints are unique to the IIS hosting environment: - Base addresses for IIS-hosted services. @@ -75,7 +79,7 @@ IIS-hosted WCF services store their configuration in the applications Web.config ### Endpoint Addresses for IIS-Hosted Services -When hosted in IIS, endpoint addresses are always considered to be relative to the address of the .svc file that represents the service. For example, if the base address of a WCF service is `http://localhost/Application1/MyService.svc` with the following endpoint configuration. +When hosted in IIS, endpoint addresses are always considered to be relative to the address of the .svc file that represents the service. For example, if the base address of a WCF service is `http://localhost/Application1/MyService.svc` with the following endpoint configuration: ```xml @@ -92,9 +96,11 @@ Similarly, the endpoint configuration element that uses an empty string as the r You must always use relative endpoint addresses for IIS-hosted service endpoints. Supplying a fully-qualified endpoint address (for example, `http://localhost/MyService.svc`) can lead to errors in the deployment of the service if the endpoint address does not point to the IIS-application that hosts the service exposing the endpoint. Using relative endpoint addresses for hosted services avoids these potential conflicts. ### Available Transports + WCF services hosted in IIS 5.1 and [!INCLUDE[iis601](../../../../includes/iis601-md.md)] are restricted to using HTTP-based communication. On these IIS platforms, configuring a hosted service to use a non-HTTP binding results in an error during service activation. For [!INCLUDE[iisver](../../../../includes/iisver-md.md)], the supported transports include HTTP, Net.TCP, Net.Pipe, Net.MSMQ, and msmq.formatname for backwards compatibility with existing MSMQ applications. ### HTTP Transport Security + IIS-hosted WCF services can make use of HTTP transport security (for example, HTTPS and HTTP authentication schemes such as Basic, Digest, and Windows Integrated Authentication) as long as the IIS virtual directory that contains the service supports those settings. The HTTP Transport Security settings on a hosted endpoint’s binding must match the transport security settings on the IIS virtual directory that contains it. For example, a WCF endpoint configured to use HTTP digest authentication must reside in an IIS virtual directory that is also configured to allow HTTP digest authentication. Unmatched combinations of IIS settings and WCF endpoint settings result in an error during service activation. diff --git a/docs/framework/wcf/feature-details/media/windows-features-iis.png b/docs/framework/wcf/feature-details/media/windows-features-iis.png new file mode 100644 index 0000000000000..820936170637c Binary files /dev/null and b/docs/framework/wcf/feature-details/media/windows-features-iis.png differ diff --git a/docs/standard/asynchronous-programming-patterns/calling-synchronous-methods-asynchronously.md b/docs/standard/asynchronous-programming-patterns/calling-synchronous-methods-asynchronously.md index 85289dc557b26..e05defbb44101 100644 --- a/docs/standard/asynchronous-programming-patterns/calling-synchronous-methods-asynchronously.md +++ b/docs/standard/asynchronous-programming-patterns/calling-synchronous-methods-asynchronously.md @@ -2,11 +2,11 @@ title: "Calling Synchronous Methods Asynchronously" ms.date: "03/30/2017" ms.technology: dotnet-standard -dev_langs: +dev_langs: - "csharp" - "vb" - "cpp" -helpviewer_keywords: +helpviewer_keywords: - "asynchronous programming, delegates" - "asynchronous delegates" - "AsyncWaitHandle property" @@ -24,87 +24,88 @@ author: "rpetrusha" ms.author: "ronpet" --- # Calling Synchronous Methods Asynchronously -The .NET Framework enables you to call any method asynchronously. To do this you define a delegate with the same signature as the method you want to call; the common language runtime automatically defines `BeginInvoke` and `EndInvoke` methods for this delegate, with the appropriate signatures. - + +The .NET Framework enables you to call any method asynchronously. To do this you define a delegate with the same signature as the method you want to call; the common language runtime automatically defines `BeginInvoke` and `EndInvoke` methods for this delegate, with the appropriate signatures. + > [!NOTE] -> Asynchronous delegate calls, specifically the `BeginInvoke` and `EndInvoke` methods, are not supported in the .NET Compact Framework. - - The `BeginInvoke` method initiates the asynchronous call. It has the same parameters as the method that you want to execute asynchronously, plus two additional optional parameters. The first parameter is an delegate that references a method to be called when the asynchronous call completes. The second parameter is a user-defined object that passes information into the callback method. `BeginInvoke` returns immediately and does not wait for the asynchronous call to complete. `BeginInvoke` returns an , which can be used to monitor the progress of the asynchronous call. - - The `EndInvoke` method retrieves the results of the asynchronous call. It can be called any time after `BeginInvoke`. If the asynchronous call has not completed, `EndInvoke` blocks the calling thread until it completes. The parameters of `EndInvoke` include the `out` and `ref` parameters (`` `ByRef` and `ByRef` in Visual Basic) of the method that you want to execute asynchronously, plus the returned by `BeginInvoke`. - +> Asynchronous delegate calls, specifically the `BeginInvoke` and `EndInvoke` methods, are not supported in the .NET Compact Framework. + +The `BeginInvoke` method initiates the asynchronous call. It has the same parameters as the method that you want to execute asynchronously, plus two additional optional parameters. The first parameter is an delegate that references a method to be called when the asynchronous call completes. The second parameter is a user-defined object that passes information into the callback method. `BeginInvoke` returns immediately and does not wait for the asynchronous call to complete. `BeginInvoke` returns an , which can be used to monitor the progress of the asynchronous call. + +The `EndInvoke` method retrieves the results of the asynchronous call. It can be called any time after `BeginInvoke`. If the asynchronous call has not completed, `EndInvoke` blocks the calling thread until it completes. The parameters of `EndInvoke` include the `out` and `ref` parameters (`` `ByRef` and `ByRef` in Visual Basic) of the method that you want to execute asynchronously, plus the returned by `BeginInvoke`. + > [!NOTE] -> The IntelliSense feature in [!INCLUDE[vsprvslong](../../../includes/vsprvslong-md.md)] displays the parameters of `BeginInvoke` and `EndInvoke`. If you are not using Visual Studio or a similar tool, or if you are using C# with [!INCLUDE[vsprvslong](../../../includes/vsprvslong-md.md)], see [Asynchronous Programming Model (APM)](../../../docs/standard/asynchronous-programming-patterns/asynchronous-programming-model-apm.md) for a description of the parameters defined for these methods. - - The code examples in this topic demonstrate four common ways to use `BeginInvoke` and `EndInvoke` to make asynchronous calls. After calling `BeginInvoke` you can do the following: - -- Do some work and then call `EndInvoke` to block until the call completes. - -- Obtain a using the property, use its method to block execution until the is signaled, and then call `EndInvoke`. - -- Poll the returned by `BeginInvoke` to determine when the asynchronous call has completed, and then call `EndInvoke`. - -- Pass a delegate for a callback method to `BeginInvoke`. The method is executed on a thread when the asynchronous call completes. The callback method calls `EndInvoke`. - +> The IntelliSense feature in Visual Studio displays the parameters of `BeginInvoke` and `EndInvoke`. If you're not using Visual Studio or a similar tool, or if you're using C# with Visual Studio, see [Asynchronous Programming Model (APM)](../../../docs/standard/asynchronous-programming-patterns/asynchronous-programming-model-apm.md) for a description of the parameters defined for these methods. + +The code examples in this topic demonstrate four common ways to use `BeginInvoke` and `EndInvoke` to make asynchronous calls. After calling `BeginInvoke` you can do the following: + +- Do some work and then call `EndInvoke` to block until the call completes. + +- Obtain a using the property, use its method to block execution until the is signaled, and then call `EndInvoke`. + +- Poll the returned by `BeginInvoke` to determine when the asynchronous call has completed, and then call `EndInvoke`. + +- Pass a delegate for a callback method to `BeginInvoke`. The method is executed on a thread when the asynchronous call completes. The callback method calls `EndInvoke`. + > [!IMPORTANT] -> No matter which technique you use, always call `EndInvoke` to complete your asynchronous call. - -## Defining the Test Method and Asynchronous Delegate - The code examples that follow demonstrate various ways of calling the same long-running method, `TestMethod`, asynchronously. The `TestMethod` method displays a console message to show that it has begun processing, sleeps for a few seconds, and then ends. `TestMethod` has an `out` parameter to demonstrate the way such parameters are added to the signatures of `BeginInvoke` and `EndInvoke`. You can handle `ref` parameters similarly. - - The following code example shows the definition of `TestMethod` and the delegate named `AsyncMethodCaller` that can be used to call `TestMethod` asynchronously. To compile the code examples, you must include the definitions for `TestMethod` and the `AsyncMethodCaller` delegate. - +> No matter which technique you use, always call `EndInvoke` to complete your asynchronous call. + +## Defining the Test Method and Asynchronous Delegate + The code examples that follow demonstrate various ways of calling the same long-running method, `TestMethod`, asynchronously. The `TestMethod` method displays a console message to show that it has begun processing, sleeps for a few seconds, and then ends. `TestMethod` has an `out` parameter to demonstrate the way such parameters are added to the signatures of `BeginInvoke` and `EndInvoke`. You can handle `ref` parameters similarly. + + The following code example shows the definition of `TestMethod` and the delegate named `AsyncMethodCaller` that can be used to call `TestMethod` asynchronously. To compile the code examples, you must include the definitions for `TestMethod` and the `AsyncMethodCaller` delegate. + [!code-cpp[AsyncDelegateExamples#1](../../../samples/snippets/cpp/VS_Snippets_CLR/AsyncDelegateExamples/cpp/TestMethod.cpp#1)] [!code-csharp[AsyncDelegateExamples#1](../../../samples/snippets/csharp/VS_Snippets_CLR/AsyncDelegateExamples/CS/TestMethod.cs#1)] - [!code-vb[AsyncDelegateExamples#1](../../../samples/snippets/visualbasic/VS_Snippets_CLR/AsyncDelegateExamples/VB/TestMethod.vb#1)] - -## Waiting for an Asynchronous Call with EndInvoke - The simplest way to execute a method asynchronously is to start executing the method by calling the delegate's `BeginInvoke` method, do some work on the main thread, and then call the delegate's `EndInvoke` method. `EndInvoke` might block the calling thread because it does not return until the asynchronous call completes. This is a good technique to use with file or network operations. - + [!code-vb[AsyncDelegateExamples#1](../../../samples/snippets/visualbasic/VS_Snippets_CLR/AsyncDelegateExamples/VB/TestMethod.vb#1)] + +## Waiting for an Asynchronous Call with EndInvoke + The simplest way to execute a method asynchronously is to start executing the method by calling the delegate's `BeginInvoke` method, do some work on the main thread, and then call the delegate's `EndInvoke` method. `EndInvoke` might block the calling thread because it does not return until the asynchronous call completes. This is a good technique to use with file or network operations. + > [!IMPORTANT] -> Because `EndInvoke` might block, you should never call it from threads that service the user interface. - +> Because `EndInvoke` might block, you should never call it from threads that service the user interface. + [!code-cpp[AsyncDelegateExamples#2](../../../samples/snippets/cpp/VS_Snippets_CLR/AsyncDelegateExamples/cpp/EndInvoke.cpp#2)] [!code-csharp[AsyncDelegateExamples#2](../../../samples/snippets/csharp/VS_Snippets_CLR/AsyncDelegateExamples/CS/EndInvoke.cs#2)] - [!code-vb[AsyncDelegateExamples#2](../../../samples/snippets/visualbasic/VS_Snippets_CLR/AsyncDelegateExamples/VB/EndInvoke.vb#2)] - -## Waiting for an Asynchronous Call with WaitHandle - You can obtain a by using the property of the returned by `BeginInvoke`. The is signaled when the asynchronous call completes, and you can wait for it by calling the method. - - If you use a , you can perform additional processing before or after the asynchronous call completes, but before calling `EndInvoke` to retrieve the results. - + [!code-vb[AsyncDelegateExamples#2](../../../samples/snippets/visualbasic/VS_Snippets_CLR/AsyncDelegateExamples/VB/EndInvoke.vb#2)] + +## Waiting for an Asynchronous Call with WaitHandle + You can obtain a by using the property of the returned by `BeginInvoke`. The is signaled when the asynchronous call completes, and you can wait for it by calling the method. + + If you use a , you can perform additional processing before or after the asynchronous call completes, but before calling `EndInvoke` to retrieve the results. + > [!NOTE] -> The wait handle is not closed automatically when you call `EndInvoke`. If you release all references to the wait handle, system resources are freed when garbage collection reclaims the wait handle. To free the system resources as soon as you are finished using the wait handle, dispose of it by calling the method. Garbage collection works more efficiently when disposable objects are explicitly disposed. - +> The wait handle is not closed automatically when you call `EndInvoke`. If you release all references to the wait handle, system resources are freed when garbage collection reclaims the wait handle. To free the system resources as soon as you are finished using the wait handle, dispose of it by calling the method. Garbage collection works more efficiently when disposable objects are explicitly disposed. + [!code-cpp[AsyncDelegateExamples#3](../../../samples/snippets/cpp/VS_Snippets_CLR/AsyncDelegateExamples/cpp/waithandle.cpp#3)] [!code-csharp[AsyncDelegateExamples#3](../../../samples/snippets/csharp/VS_Snippets_CLR/AsyncDelegateExamples/CS/waithandle.cs#3)] - [!code-vb[AsyncDelegateExamples#3](../../../samples/snippets/visualbasic/VS_Snippets_CLR/AsyncDelegateExamples/VB/WaitHandle.vb#3)] - -## Polling for Asynchronous Call Completion - You can use the property of the returned by `BeginInvoke` to discover when the asynchronous call completes. You might do this when making the asynchronous call from a thread that services the user interface. Polling for completion allows the calling thread to continue executing while the asynchronous call executes on a thread. - + [!code-vb[AsyncDelegateExamples#3](../../../samples/snippets/visualbasic/VS_Snippets_CLR/AsyncDelegateExamples/VB/WaitHandle.vb#3)] + +## Polling for Asynchronous Call Completion + You can use the property of the returned by `BeginInvoke` to discover when the asynchronous call completes. You might do this when making the asynchronous call from a thread that services the user interface. Polling for completion allows the calling thread to continue executing while the asynchronous call executes on a thread. + [!code-cpp[AsyncDelegateExamples#4](../../../samples/snippets/cpp/VS_Snippets_CLR/AsyncDelegateExamples/cpp/polling.cpp#4)] [!code-csharp[AsyncDelegateExamples#4](../../../samples/snippets/csharp/VS_Snippets_CLR/AsyncDelegateExamples/CS/polling.cs#4)] - [!code-vb[AsyncDelegateExamples#4](../../../samples/snippets/visualbasic/VS_Snippets_CLR/AsyncDelegateExamples/VB/polling.vb#4)] - -## Executing a Callback Method When an Asynchronous Call Completes - If the thread that initiates the asynchronous call does not need to be the thread that processes the results, you can execute a callback method when the call completes. The callback method is executed on a thread. - - To use a callback method, you must pass `BeginInvoke` an delegate that represents the callback method. You can also pass an object that contains information to be used by the callback method. In the callback method, you can cast the , which is the only parameter of the callback method, to an object. You can then use the property to get the delegate that was used to initiate the call so that you can call `EndInvoke`. - - Notes on the example: - -- The `threadId` parameter of `TestMethod` is an `out` parameter ([`` `ByRef` in Visual Basic), so its input value is never used by `TestMethod`. A dummy variable is passed to the `BeginInvoke` call. If the `threadId` parameter were a `ref` parameter (`ByRef` in Visual Basic), the variable would have to be a class-level field so that it could be passed to both `BeginInvoke` and `EndInvoke`. - -- The state information that is passed to `BeginInvoke` is a format string, which the callback method uses to format an output message. Because it is passed as type , the state information must be cast to its proper type before it can be used. - -- The callback is made on a thread. threads are background threads, which do not keep the application running if the main thread ends, so the main thread of the example has to sleep long enough for the callback to finish. - + [!code-vb[AsyncDelegateExamples#4](../../../samples/snippets/visualbasic/VS_Snippets_CLR/AsyncDelegateExamples/VB/polling.vb#4)] + +## Executing a Callback Method When an Asynchronous Call Completes + If the thread that initiates the asynchronous call does not need to be the thread that processes the results, you can execute a callback method when the call completes. The callback method is executed on a thread. + + To use a callback method, you must pass `BeginInvoke` an delegate that represents the callback method. You can also pass an object that contains information to be used by the callback method. In the callback method, you can cast the , which is the only parameter of the callback method, to an object. You can then use the property to get the delegate that was used to initiate the call so that you can call `EndInvoke`. + + Notes on the example: + +- The `threadId` parameter of `TestMethod` is an `out` parameter ([`` `ByRef` in Visual Basic), so its input value is never used by `TestMethod`. A dummy variable is passed to the `BeginInvoke` call. If the `threadId` parameter were a `ref` parameter (`ByRef` in Visual Basic), the variable would have to be a class-level field so that it could be passed to both `BeginInvoke` and `EndInvoke`. + +- The state information that is passed to `BeginInvoke` is a format string, which the callback method uses to format an output message. Because it is passed as type , the state information must be cast to its proper type before it can be used. + +- The callback is made on a thread. threads are background threads, which do not keep the application running if the main thread ends, so the main thread of the example has to sleep long enough for the callback to finish. + [!code-cpp[AsyncDelegateExamples#5](../../../samples/snippets/cpp/VS_Snippets_CLR/AsyncDelegateExamples/cpp/callback.cpp#5)] [!code-csharp[AsyncDelegateExamples#5](../../../samples/snippets/csharp/VS_Snippets_CLR/AsyncDelegateExamples/CS/callback.cs#5)] - [!code-vb[AsyncDelegateExamples#5](../../../samples/snippets/visualbasic/VS_Snippets_CLR/AsyncDelegateExamples/VB/callback.vb#5)] - + [!code-vb[AsyncDelegateExamples#5](../../../samples/snippets/visualbasic/VS_Snippets_CLR/AsyncDelegateExamples/VB/callback.vb#5)] + ## See also -- +- - [Event-based Asynchronous Pattern (EAP)](../../../docs/standard/asynchronous-programming-patterns/event-based-asynchronous-pattern-eap.md) diff --git a/docs/standard/asynchronous-programming-patterns/consuming-the-task-based-asynchronous-pattern.md b/docs/standard/asynchronous-programming-patterns/consuming-the-task-based-asynchronous-pattern.md index 146ff7b864cec..79ca6eb00fd5f 100644 --- a/docs/standard/asynchronous-programming-patterns/consuming-the-task-based-asynchronous-pattern.md +++ b/docs/standard/asynchronous-programming-patterns/consuming-the-task-based-asynchronous-pattern.md @@ -2,7 +2,7 @@ title: "Consuming the Task-based Asynchronous Pattern" ms.date: "03/30/2017" ms.technology: dotnet-standard -helpviewer_keywords: +helpviewer_keywords: - ".NET Framework, and TAP" - "asynchronous design patterns, .NET Framework" - "TAP, .NET Framework support for" @@ -13,823 +13,824 @@ author: "rpetrusha" ms.author: "ronpet" --- # Consuming the Task-based Asynchronous Pattern -When you use the Task-based Asynchronous Pattern (TAP) to work with asynchronous operations, you can use callbacks to achieve waiting without blocking. For tasks, this is achieved through methods such as . Language-based asynchronous support hides callbacks by allowing asynchronous operations to be awaited within normal control flow, and compiler-generated code provides this same API-level support. - -## Suspending Execution with Await - Starting with the [!INCLUDE[net_v45](../../../includes/net-v45-md.md)], you can use the [await](~/docs/csharp/language-reference/keywords/await.md) keyword in C# and the [Await Operator](~/docs/visual-basic/language-reference/operators/await-operator.md) in Visual Basic to asynchronously await and objects. When you're awaiting a , the `await` expression is of type `void`. When you're awaiting a , the `await` expression is of type `TResult`. An `await` expression must occur inside the body of an asynchronous method. For more information about C# and Visual Basic language support in the [!INCLUDE[net_v45](../../../includes/net-v45-md.md)], see the C# and Visual Basic language specifications. - - Under the covers, the await functionality installs a callback on the task by using a continuation. This callback resumes the asynchronous method at the point of suspension. When the asynchronous method is resumed, if the awaited operation completed successfully and was a , its `TResult` is returned. If the or that was awaited ended in the state, an exception is thrown. If the or that was awaited ended in the state, the exception that caused it to fault is thrown. A `Task` can fault as a result of multiple exceptions, but only one of these exceptions is propagated. However, the property returns an exception that contains all the errors. - - If a synchronization context ( object) is associated with the thread that was executing the asynchronous method at the time of suspension (for example, if the property is not `null`), the asynchronous method resumes on that same synchronization context by using the context’s method. Otherwise, it relies on the task scheduler ( object) that was current at the time of suspension. Typically, this is the default task scheduler (), which targets the thread pool. This task scheduler determines whether the awaited asynchronous operation should resume where it completed or whether the resumption should be scheduled. The default scheduler typically allows the continuation to run on the thread that the awaited operation completed. - - When an asynchronous method is called, it synchronously executes the body of the function up until the first await expression on an awaitable instance that has not yet completed, at which point the invocation returns to the caller. If the asynchronous method does not return `void`, a or object is returned to represent the ongoing computation. In a non-void asynchronous method, if a return statement is encountered or the end of the method body is reached, the task is completed in the final state. If an unhandled exception causes control to leave the body of the asynchronous method, the task ends in the state. If that exception is an , the task instead ends in the state. In this manner, the result or exception is eventually published. - - There are several important variations of this behavior. For performance reasons, if a task has already completed by the time the task is awaited, control is not yielded, and the function continues to execute. Additionally, returning to the original context isn't always the desired behavior and can be changed; this is described in more detail in the next section. - -### Configuring Suspension and Resumption with Yield and ConfigureAwait - Several methods provide more control over an asynchronous method’s execution. For example, you can use the method to introduce a yield point into the asynchronous method: - -```csharp -public class Task : … -{ - public static YieldAwaitable Yield(); - … -} -``` - - This is equivalent to asynchronously posting or scheduling back to the current context. - -```csharp -Task.Run(async delegate -{ - for(int i=0; i<1000000; i++) - { - await Task.Yield(); // fork the continuation into a separate work item - ... - } -}); -``` - - You can also use the method for better control over suspension and resumption in an asynchronous method. As mentioned previously, by default, the current context is captured at the time an asynchronous method is suspended, and that captured context is used to invoke the asynchronous method’s continuation upon resumption. In many cases, this is the exact behavior you want. In other cases, you may not care about the continuation context, and you can achieve better performance by avoiding such posts back to the original context. To enable this, use the method to inform the await operation not to capture and resume on the context, but to continue execution wherever the asynchronous operation that was being awaited completed: - -```csharp -await someTask.ConfigureAwait(continueOnCapturedContext:false); -``` - -## Canceling an Asynchronous Operation - Starting with the [!INCLUDE[net_v40_long](../../../includes/net-v40-long-md.md)], TAP methods that support cancellation provide at least one overload that accepts a cancellation token ( object). - - A cancellation token is created through a cancellation token source ( object). The source’s property returns the cancellation token that will be signaled when the source’s method is called. For example, if you want to download a single webpage and you want to be able to cancel the operation, you create a object, pass its token to the TAP method, and then call the source’s method when you're ready to cancel the operation: - -```csharp -var cts = new CancellationTokenSource(); -string result = await DownloadStringAsync(url, cts.Token); -… // at some point later, potentially on another thread -cts.Cancel(); -``` - - To cancel multiple asynchronous invocations, you can pass the same token to all invocations: - -```csharp -var cts = new CancellationTokenSource(); - IList results = await Task.WhenAll(from url in urls select DownloadStringAsync(url, cts.Token)); - // at some point later, potentially on another thread - … - cts.Cancel(); -``` - - Or, you can pass the same token to a selective subset of operations: - -```csharp -var cts = new CancellationTokenSource(); - byte [] data = await DownloadDataAsync(url, cts.Token); - await SaveToDiskAsync(outputPath, data, CancellationToken.None); - … // at some point later, potentially on another thread - cts.Cancel(); -``` - - Cancellation requests may be initiated from any thread. - - You can pass the value to any method that accepts a cancellation token to indicate that cancellation will never be requested. This causes the property to return `false`, and the called method can optimize accordingly. For testing purposes, you can also pass in a pre-canceled cancellation token that is instantiated by using the constructor that accepts a Boolean value to indicate whether the token should start in an already-canceled or not-cancelable state. - - This approach to cancellation has several advantages: - -- You can pass the same cancellation token to any number of asynchronous and synchronous operations. - -- The same cancellation request may be proliferated to any number of listeners. - -- The developer of the asynchronous API is in complete control of whether cancellation may be requested and when it may take effect. - -- The code that consumes the API may selectively determine the asynchronous invocations that cancellation requests will be propagated to. - -## Monitoring Progress - Some asynchronous methods expose progress through a progress interface passed into the asynchronous method. For example, consider a function which asynchronously downloads a string of text, and along the way raises progress updates that include the percentage of the download that has completed thus far. Such a method could be consumed in a Windows Presentation Foundation (WPF) application as follows: - -```csharp -private async void btnDownload_Click(object sender, RoutedEventArgs e) -{ - btnDownload.IsEnabled = false; - try - { - txtResult.Text = await DownloadStringAsync(txtUrl.Text, - new Progress(p => pbDownloadProgress.Value = p)); - } - finally { btnDownload.IsEnabled = true; } -} -``` - - -## Using the Built-in Task-based Combinators - The namespace includes several methods for composing and working with tasks. - -### Task.Run - The class includes several methods that let you easily offload work as a or to the thread pool, for example: - -```csharp -public async void button1_Click(object sender, EventArgs e) -{ - textBox1.Text = await Task.Run(() => - { - // … do compute-bound work here - return answer; - }); -} -``` - - Some of these methods, such as the overload, exist as shorthand for the method. Other overloads, such as , enable you to use await within the offloaded work, for example: - -```csharp -public async void button1_Click(object sender, EventArgs e) -{ - pictureBox1.Image = await Task.Run(async() => - { - using(Bitmap bmp1 = await DownloadFirstImageAsync()) - using(Bitmap bmp2 = await DownloadSecondImageAsync()) - return Mashup(bmp1, bmp2); - }); -} -``` - - Such overloads are logically equivalent to using the method in conjunction with the extension method in the Task Parallel Library. - -### Task.FromResult - Use the method in scenarios where data may already be available and just needs to be returned from a task-returning method lifted into a : - -```csharp -public Task GetValueAsync(string key) -{ - int cachedValue; - return TryGetCachedValue(out cachedValue) ? - Task.FromResult(cachedValue) : - GetValueAsyncInternal(); -} - -private async Task GetValueAsyncInternal(string key) -{ - … -} -``` - -### Task.WhenAll - Use the method to asynchronously wait on multiple asynchronous operations that are represented as tasks. The method has multiple overloads that support a set of non-generic tasks or a non-uniform set of generic tasks (for example, asynchronously waiting for multiple void-returning operations, or asynchronously waiting for multiple value-returning methods where each value may have a different type) and to support a uniform set of generic tasks (such as asynchronously waiting for multiple `TResult`-returning methods). - - Let's say you want to send email messages to several customers. You can overlap sending the messages so you're not waiting for one message to complete before sending the next. You can also find out when the send operations have completed and whether any errors have occurred: - -```csharp -IEnumerable asyncOps = from addr in addrs select SendMailAsync(addr); -await Task.WhenAll(asyncOps); -``` - - This code doesn't explicitly handle exceptions that may occur, but lets exceptions propagate out of the `await` on the resulting task from . To handle the exceptions, you can use code such as the following: - -```csharp -IEnumerable asyncOps = from addr in addrs select SendMailAsync(addr); -try -{ - await Task.WhenAll(asyncOps); -} -catch(Exception exc) -{ - ... -} -``` - - In this case, if any asynchronous operation fails, all the exceptions will be consolidated in an exception, which is stored in the that is returned from the method. However, only one of those exceptions is propagated by the `await` keyword. If you want to examine all the exceptions, you can rewrite the previous code as follows: - -```csharp -Task [] asyncOps = (from addr in addrs select SendMailAsync(addr)).ToArray(); -try -{ - await Task.WhenAll(asyncOps); -} -catch(Exception exc) -{ - foreach(Task faulted in asyncOps.Where(t => t.IsFaulted)) - { - … // work with faulted and faulted.Exception - } -} -``` - - Let's consider an example of downloading multiple files from the web asynchronously. In this case, all the asynchronous operations have homogeneous result types, and it's easy to access the results: - -```csharp -string [] pages = await Task.WhenAll( - from url in urls select DownloadStringAsync(url)); -``` - - You can use the same exception-handling techniques we discussed in the previous void-returning scenario: - -```csharp -Task [] asyncOps = - (from url in urls select DownloadStringAsync(url)).ToArray(); -try -{ - string [] pages = await Task.WhenAll(asyncOps); - ... -} -catch(Exception exc) -{ - foreach(Task faulted in asyncOps.Where(t => t.IsFaulted)) - { - … // work with faulted and faulted.Exception - } -} -``` - -### Task.WhenAny - You can use the method to asynchronously wait for just one of multiple asynchronous operations represented as tasks to complete. This method serves four primary use cases: - -- Redundancy: Performing an operation multiple times and selecting the one that completes first (for example, contacting multiple stock quote web services that will produce a single result and selecting the one that completes the fastest). - -- Interleaving: Launching multiple operations and waiting for all of them to complete, but processing them as they complete. - -- Throttling: Allowing additional operations to begin as others complete. This is an extension of the interleaving scenario. - -- Early bailout: For example, an operation represented by task t1 can be grouped in a task with another task t2, and you can wait on the task. Task t2 could represent a time-out, or cancellation, or some other signal that causes the task to complete before t1 completes. - -#### Redundancy - Consider a case where you want to make a decision about whether to buy a stock. There are several stock recommendation web services that you trust, but depending on daily load, each service can end up being slow at different times. You can use the method to receive a notification when any operation completes: - -```csharp -var recommendations = new List>() -{ - GetBuyRecommendation1Async(symbol), - GetBuyRecommendation2Async(symbol), - GetBuyRecommendation3Async(symbol) -}; -Task recommendation = await Task.WhenAny(recommendations); -if (await recommendation) BuyStock(symbol); -``` - - Unlike , which returns the unwrapped results of all tasks that completed successfully, returns the task that completed. If a task fails, it’s important to know that it failed, and if a task succeeds, it’s important to know which task the return value is associated with. Therefore, you need to access the result of the returned task, or further await it, as this example shows. - - As with , you have to be able to accommodate exceptions. Because you receive the completed task back, you can await the returned task to have errors propagated, and `try/catch` them appropriately; for example: - -```csharp -Task [] recommendations = …; -while(recommendations.Count > 0) -{ - Task recommendation = await Task.WhenAny(recommendations); - try - { - if (await recommendation) BuyStock(symbol); - break; - } - catch(WebException exc) - { - recommendations.Remove(recommendation); - } -} -``` - - Additionally, even if a first task completes successfully, subsequent tasks may fail. At this point, you have several options for dealing with exceptions: You can wait until all the launched tasks have completed, in which case you can use the method, or you can decide that all exceptions are important and must be logged. For this, you can use continuations to receive a notification when tasks have completed asynchronously: - -```csharp -foreach(Task recommendation in recommendations) -{ - var ignored = recommendation.ContinueWith( - t => { if (t.IsFaulted) Log(t.Exception); }); -} -``` - - or: - -```csharp -foreach(Task recommendation in recommendations) -{ - var ignored = recommendation.ContinueWith( - t => Log(t.Exception), TaskContinuationOptions.OnlyOnFaulted); -} -``` - - or even: - -``` -private static async void LogCompletionIfFailed(IEnumerable tasks) -{ - foreach(var task in tasks) - { - try { await task; } - catch(Exception exc) { Log(exc); } - } -} -… -LogCompletionIfFailed(recommendations); -``` - - Finally, you may want to cancel all the remaining operations: - -```csharp -var cts = new CancellationTokenSource(); -var recommendations = new List>() -{ - GetBuyRecommendation1Async(symbol, cts.Token), - GetBuyRecommendation2Async(symbol, cts.Token), - GetBuyRecommendation3Async(symbol, cts.Token) -}; - -Task recommendation = await Task.WhenAny(recommendations); -cts.Cancel(); -if (await recommendation) BuyStock(symbol); -``` - -#### Interleaving - Consider a case where you're downloading images from the web and processing each image (for example, adding the image to a UI control). You have to do the processing sequentially on the UI thread, but you want to download the images as concurrently as possible. Also, you don’t want to hold up adding the images to the UI until they’re all downloaded—you want to add them as they complete: - -```csharp -List> imageTasks = - (from imageUrl in urls select GetBitmapAsync(imageUrl)).ToList(); -while(imageTasks.Count > 0) -{ - try - { - Task imageTask = await Task.WhenAny(imageTasks); - imageTasks.Remove(imageTask); - - Bitmap image = await imageTask; - panel.AddImage(image); - } - catch{} -} -``` - - You can also apply interleaving to a scenario that involves computationally intensive processing on the of the downloaded images; for example: - -```csharp -List> imageTasks = - (from imageUrl in urls select GetBitmapAsync(imageUrl) - .ContinueWith(t => ConvertImage(t.Result)).ToList(); -while(imageTasks.Count > 0) -{ - try - { - Task imageTask = await Task.WhenAny(imageTasks); - imageTasks.Remove(imageTask); - - Bitmap image = await imageTask; - panel.AddImage(image); - } - catch{} -} -``` - -#### Throttling - Consider the interleaving example, except that the user is downloading so many images that the downloads have to be throttled; for example, you want only a specific number of downloads to happen concurrently. To achieve this, you can start a subset of the asynchronous operations. As operations complete, you can start additional operations to take their place: - -```csharp -const int CONCURRENCY_LEVEL = 15; -Uri [] urls = …; -int nextIndex = 0; -var imageTasks = new List>(); -while(nextIndex < CONCURRENCY_LEVEL && nextIndex < urls.Length) -{ - imageTasks.Add(GetBitmapAsync(urls[nextIndex])); - nextIndex++; -} - -while(imageTasks.Count > 0) -{ - try - { - Task imageTask = await Task.WhenAny(imageTasks); - imageTasks.Remove(imageTask); - - Bitmap image = await imageTask; - panel.AddImage(image); - } - catch(Exception exc) { Log(exc); } - - if (nextIndex < urls.Length) - { - imageTasks.Add(GetBitmapAsync(urls[nextIndex])); - nextIndex++; - } -} -``` - -#### Early Bailout - Consider that you're waiting asynchronously for an operation to complete while simultaneously responding to a user’s cancellation request (for example, the user clicked a cancel button). The following code illustrates this scenario: - -```csharp -private CancellationTokenSource m_cts; - -public void btnCancel_Click(object sender, EventArgs e) -{ - if (m_cts != null) m_cts.Cancel(); -} - -public async void btnRun_Click(object sender, EventArgs e) -{ - m_cts = new CancellationTokenSource(); - btnRun.Enabled = false; - try - { - Task imageDownload = GetBitmapAsync(txtUrl.Text); - await UntilCompletionOrCancellation(imageDownload, m_cts.Token); - if (imageDownload.IsCompleted) - { - Bitmap image = await imageDownload; - panel.AddImage(image); - } - else imageDownload.ContinueWith(t => Log(t)); - } - finally { btnRun.Enabled = true; } -} - -private static async Task UntilCompletionOrCancellation( - Task asyncOp, CancellationToken ct) -{ - var tcs = new TaskCompletionSource(); - using(ct.Register(() => tcs.TrySetResult(true))) - await Task.WhenAny(asyncOp, tcs.Task); - return asyncOp; -} -``` - - This implementation re-enables the user interface as soon as you decide to bail out, but doesn't cancel the underlying asynchronous operations. Another alternative would be to cancel the pending operations when you decide to bail out, but not reestablish the user interface until the operations actually complete, potentially due to ending early due to the cancellation request: - -```csharp -private CancellationTokenSource m_cts; - -public async void btnRun_Click(object sender, EventArgs e) -{ - m_cts = new CancellationTokenSource(); - - btnRun.Enabled = false; - try - { - Task imageDownload = GetBitmapAsync(txtUrl.Text, m_cts.Token); - await UntilCompletionOrCancellation(imageDownload, m_cts.Token); - Bitmap image = await imageDownload; - panel.AddImage(image); - } - catch(OperationCanceledException) {} - finally { btnRun.Enabled = true; } -} -``` - - Another example of early bailout involves using the method in conjunction with the method, as discussed in the next section. - -### Task.Delay - You can use the method to introduce pauses into an asynchronous method’s execution. This is useful for many kinds of functionality, including building polling loops and delaying the handling of user input for a predetermined period of time. The method can also be useful in combination with for implementing time-outs on awaits. - - If a task that’s part of a larger asynchronous operation (for example, an ASP.NET web service) takes too long to complete, the overall operation could suffer, especially if it fails to ever complete. For this reason, it’s important to be able to time out when waiting on an asynchronous operation. The synchronous , , and methods accept time-out values, but the corresponding / and the previously mentioned / methods do not. Instead, you can use and in combination to implement a time-out. - - For example, in your UI application, let's say that you want to download an image and disable the UI while the image is downloading. However, if the download takes too long, you want to re-enable the UI and discard the download: - -```csharp -public async void btnDownload_Click(object sender, EventArgs e) -{ - btnDownload.Enabled = false; - try - { - Task download = GetBitmapAsync(url); - if (download == await Task.WhenAny(download, Task.Delay(3000))) - { - Bitmap bmp = await download; - pictureBox.Image = bmp; - status.Text = "Downloaded"; - } - else - { - pictureBox.Image = null; - status.Text = "Timed out"; - var ignored = download.ContinueWith( - t => Trace("Task finally completed")); - } - } - finally { btnDownload.Enabled = true; } -} -``` - - The same applies to multiple downloads, because returns a task: - -```csharp -public async void btnDownload_Click(object sender, RoutedEventArgs e) -{ - btnDownload.Enabled = false; - try - { - Task downloads = - Task.WhenAll(from url in urls select GetBitmapAsync(url)); - if (downloads == await Task.WhenAny(downloads, Task.Delay(3000))) - { - foreach(var bmp in downloads) panel.AddImage(bmp); - status.Text = "Downloaded"; - } - else - { - status.Text = "Timed out"; - downloads.ContinueWith(t => Log(t)); - } - } - finally { btnDownload.Enabled = true; } -} -``` - -## Building Task-based Combinators - Because a task is able to completely represent an asynchronous operation and provide synchronous and asynchronous capabilities for joining with the operation, retrieving its results, and so on, you can build useful libraries of combinators that compose tasks to build larger patterns. As discussed in the previous section, the .NET Framework includes several built-in combinators, but you can also build your own. The following sections provide several examples of potential combinator methods and types. - -### RetryOnFault - In many situations, you may want to retry an operation if a previous attempt fails. For synchronous code, you might build a helper method such as `RetryOnFault` in the following example to accomplish this: - -```csharp -public static T RetryOnFault( - Func function, int maxTries) -{ - for(int i=0; i RetryOnFault( - Func> function, int maxTries) -{ - for(int i=0; i DownloadStringAsync(url), 3); -``` - - You could extend the `RetryOnFault` function further. For example, the function could accept another `Func` that will be invoked between retries to determine when to try the operation again; for example: - -```csharp -public static async Task RetryOnFault( - Func> function, int maxTries, Func retryWhen) -{ - for(int i=0; i DownloadStringAsync(url), 3, () => Task.Delay(1000)); -``` - -### NeedOnlyOne - Sometimes, you can take advantage of redundancy to improve an operation’s latency and chances for success. Consider multiple web services that provide stock quotes, but at various times of the day, each service may provide different levels of quality and response times. To deal with these fluctuations, you may issue requests to all the web services, and as soon as you get a response from one, cancel the remaining requests. You can implement a helper function to make it easier to implement this common pattern of launching multiple operations, waiting for any, and then canceling the rest. The `NeedOnlyOne` function in the following example illustrates this scenario: - -```csharp -public static async Task NeedOnlyOne( - params Func> [] functions) -{ - var cts = new CancellationTokenSource(); - var tasks = (from function in functions - select function(cts.Token)).ToArray(); - var completed = await Task.WhenAny(tasks).ConfigureAwait(false); - cts.Cancel(); - foreach(var task in tasks) - { - var ignored = task.ContinueWith( - t => Log(t), TaskContinuationOptions.OnlyOnFaulted); - } - return completed; -} -``` - - You can then use this function as follows: - -```csharp -double currentPrice = await NeedOnlyOne( - ct => GetCurrentPriceFromServer1Async("msft", ct), - ct => GetCurrentPriceFromServer2Async("msft", ct), - ct => GetCurrentPriceFromServer3Async("msft", ct)); -``` - -### Interleaved Operations - There is a potential performance problem with using the method to support an interleaving scenario when you're working with very large sets of tasks. Every call to results in a continuation being registered with each task. For N number of tasks, this results in O(N2) continuations created over the lifetime of the interleaving operation. If you're working with a large set of tasks, you can use a combinator (`Interleaved` in the following example) to address the performance issue: - -```csharp -static IEnumerable> Interleaved(IEnumerable> tasks) -{ - var inputTasks = tasks.ToList(); - var sources = (from _ in Enumerable.Range(0, inputTasks.Count) - select new TaskCompletionSource()).ToList(); - int nextTaskIndex = -1; - foreach (var inputTask in inputTasks) - { - inputTask.ContinueWith(completed => - { - var source = sources[Interlocked.Increment(ref nextTaskIndex)]; - if (completed.IsFaulted) - source.TrySetException(completed.Exception.InnerExceptions); - else if (completed.IsCanceled) - source.TrySetCanceled(); - else - source.TrySetResult(completed.Result); - }, CancellationToken.None, - TaskContinuationOptions.ExecuteSynchronously, - TaskScheduler.Default); - } - return from source in sources - select source.Task; -} -``` - - You can then use the combinator to process the results of tasks as they complete; for example: - -```csharp -IEnumerable> tasks = ...; -foreach(var task in Interleaved(tasks)) -{ - int result = await task; - … -} -``` - -### WhenAllOrFirstException - In certain scatter/gather scenarios, you might want to wait for all tasks in a set, unless one of them faults, in which case you want to stop waiting as soon as the exception occurs. You can accomplish that with a combinator method such as `WhenAllOrFirstException` in the following example: - -```csharp -public static Task WhenAllOrFirstException(IEnumerable> tasks) -{ - var inputs = tasks.ToList(); - var ce = new CountdownEvent(inputs.Count); - var tcs = new TaskCompletionSource(); - - Action onCompleted = (Task completed) => - { - if (completed.IsFaulted) - tcs.TrySetException(completed.Exception.InnerExceptions); - if (ce.Signal() && !tcs.Task.IsCompleted) - tcs.TrySetResult(inputs.Select(t => t.Result).ToArray()); - }; - - foreach (var t in inputs) t.ContinueWith(onCompleted); - return tcs.Task; -} -``` - -## Building Task-based Data Structures - In addition to the ability to build custom task-based combinators, having a data structure in and that represents both the results of an asynchronous operation and the necessary synchronization to join with it makes it a very powerful type on which to build custom data structures to be used in asynchronous scenarios. - -### AsyncCache - One important aspect of a task is that it may be handed out to multiple consumers, all of whom may await it, register continuations with it, get its result or exceptions (in the case of ), and so on. This makes and perfectly suited to be used in an asynchronous caching infrastructure. Here’s an example of a small but powerful asynchronous cache built on top of : - -```csharp -public class AsyncCache -{ - private readonly Func> _valueFactory; - private readonly ConcurrentDictionary>> _map; - - public AsyncCache(Func> valueFactory) - { - if (valueFactory == null) throw new ArgumentNullException("loader"); - _valueFactory = valueFactory; - _map = new ConcurrentDictionary>>(); - } - - public Task this[TKey key] - { - get - { - if (key == null) throw new ArgumentNullException("key"); - return _map.GetOrAdd(key, toAdd => - new Lazy>(() => _valueFactory(toAdd))).Value; - } - } -} -``` - - The [AsyncCache\](https://blogs.msdn.microsoft.com/pfxteam/2010/04/23/parallelextensionsextras-tour-12-asynccache/) class accepts as a delegate to its constructor a function that takes a `TKey` and returns a . Any previously accessed values from the cache are stored in the internal dictionary, and the `AsyncCache` ensures that only one task is generated per key, even if the cache is accessed concurrently. - - For example, you can build a cache for downloaded web pages: - -```csharp -private AsyncCache m_webPages = - new AsyncCache(DownloadStringAsync); -``` - - You can then use this cache in asynchronous methods whenever you need the contents of a web page. The `AsyncCache` class ensures that you’re downloading as few pages as possible, and caches the results. - -```csharp -private async void btnDownload_Click(object sender, RoutedEventArgs e) -{ - btnDownload.IsEnabled = false; - try - { - txtContents.Text = await m_webPages["http://www.microsoft.com"]; - } - finally { btnDownload.IsEnabled = true; } -} -``` - -### AsyncProducerConsumerCollection - You can also use tasks to build data structures for coordinating asynchronous activities. Consider one of the classic parallel design patterns: producer/consumer. In this pattern, producers generate data that is consumed by consumers, and the producers and consumers may run in parallel. For example, the consumer processes item 1, which was previously generated by a producer who is now producing item 2. For the producer/consumer pattern, you invariably need some data structure to store the work created by producers so that the consumers may be notified of new data and find it when available. - - Here’s a simple data structure built on top of tasks that enables asynchronous methods to be used as producers and consumers: - -```csharp -public class AsyncProducerConsumerCollection -{ - private readonly Queue m_collection = new Queue(); - private readonly Queue> m_waiting = - new Queue>(); - - public void Add(T item) - { - TaskCompletionSource tcs = null; - lock (m_collection) - { - if (m_waiting.Count > 0) tcs = m_waiting.Dequeue(); - else m_collection.Enqueue(item); - } - if (tcs != null) tcs.TrySetResult(item); - } - - public Task Take() - { - lock (m_collection) - { - if (m_collection.Count > 0) - { - return Task.FromResult(m_collection.Dequeue()); - } - else - { - var tcs = new TaskCompletionSource(); - m_waiting.Enqueue(tcs); - return tcs.Task; - } - } - } -} -``` - - With that data structure in place, you can write code such as the following: - -```csharp -private static AsyncProducerConsumerCollection m_data = …; -… -private static async Task ConsumerAsync() -{ - while(true) - { - int nextItem = await m_data.Take(); - ProcessNextItem(nextItem); - } -} -… -private static void Produce(int data) -{ - m_data.Add(data); -} -``` - - The namespace includes the type, which you can use in a similar manner, but without having to build a custom collection type: - -```csharp -private static BufferBlock m_data = …; -… -private static async Task ConsumerAsync() -{ - while(true) - { - int nextItem = await m_data.ReceiveAsync(); - ProcessNextItem(nextItem); - } -} -… -private static void Produce(int data) -{ - m_data.Post(data); -} -``` - + +When you use the Task-based Asynchronous Pattern (TAP) to work with asynchronous operations, you can use callbacks to achieve waiting without blocking. For tasks, this is achieved through methods such as . Language-based asynchronous support hides callbacks by allowing asynchronous operations to be awaited within normal control flow, and compiler-generated code provides this same API-level support. + +## Suspending Execution with Await + Starting with the [!INCLUDE[net_v45](../../../includes/net-v45-md.md)], you can use the [await](~/docs/csharp/language-reference/keywords/await.md) keyword in C# and the [Await Operator](~/docs/visual-basic/language-reference/operators/await-operator.md) in Visual Basic to asynchronously await and objects. When you're awaiting a , the `await` expression is of type `void`. When you're awaiting a , the `await` expression is of type `TResult`. An `await` expression must occur inside the body of an asynchronous method. For more information about C# and Visual Basic language support in the [!INCLUDE[net_v45](../../../includes/net-v45-md.md)], see the C# and Visual Basic language specifications. + + Under the covers, the await functionality installs a callback on the task by using a continuation. This callback resumes the asynchronous method at the point of suspension. When the asynchronous method is resumed, if the awaited operation completed successfully and was a , its `TResult` is returned. If the or that was awaited ended in the state, an exception is thrown. If the or that was awaited ended in the state, the exception that caused it to fault is thrown. A `Task` can fault as a result of multiple exceptions, but only one of these exceptions is propagated. However, the property returns an exception that contains all the errors. + + If a synchronization context ( object) is associated with the thread that was executing the asynchronous method at the time of suspension (for example, if the property is not `null`), the asynchronous method resumes on that same synchronization context by using the context’s method. Otherwise, it relies on the task scheduler ( object) that was current at the time of suspension. Typically, this is the default task scheduler (), which targets the thread pool. This task scheduler determines whether the awaited asynchronous operation should resume where it completed or whether the resumption should be scheduled. The default scheduler typically allows the continuation to run on the thread that the awaited operation completed. + + When an asynchronous method is called, it synchronously executes the body of the function up until the first await expression on an awaitable instance that has not yet completed, at which point the invocation returns to the caller. If the asynchronous method does not return `void`, a or object is returned to represent the ongoing computation. In a non-void asynchronous method, if a return statement is encountered or the end of the method body is reached, the task is completed in the final state. If an unhandled exception causes control to leave the body of the asynchronous method, the task ends in the state. If that exception is an , the task instead ends in the state. In this manner, the result or exception is eventually published. + + There are several important variations of this behavior. For performance reasons, if a task has already completed by the time the task is awaited, control is not yielded, and the function continues to execute. Additionally, returning to the original context isn't always the desired behavior and can be changed; this is described in more detail in the next section. + +### Configuring Suspension and Resumption with Yield and ConfigureAwait + Several methods provide more control over an asynchronous method’s execution. For example, you can use the method to introduce a yield point into the asynchronous method: + +```csharp +public class Task : … +{ + public static YieldAwaitable Yield(); + … +} +``` + + This is equivalent to asynchronously posting or scheduling back to the current context. + +```csharp +Task.Run(async delegate +{ + for(int i=0; i<1000000; i++) + { + await Task.Yield(); // fork the continuation into a separate work item + ... + } +}); +``` + + You can also use the method for better control over suspension and resumption in an asynchronous method. As mentioned previously, by default, the current context is captured at the time an asynchronous method is suspended, and that captured context is used to invoke the asynchronous method’s continuation upon resumption. In many cases, this is the exact behavior you want. In other cases, you may not care about the continuation context, and you can achieve better performance by avoiding such posts back to the original context. To enable this, use the method to inform the await operation not to capture and resume on the context, but to continue execution wherever the asynchronous operation that was being awaited completed: + +```csharp +await someTask.ConfigureAwait(continueOnCapturedContext:false); +``` + +## Canceling an Asynchronous Operation + Starting with the [!INCLUDE[net_v40_long](../../../includes/net-v40-long-md.md)], TAP methods that support cancellation provide at least one overload that accepts a cancellation token ( object). + + A cancellation token is created through a cancellation token source ( object). The source’s property returns the cancellation token that will be signaled when the source’s method is called. For example, if you want to download a single webpage and you want to be able to cancel the operation, you create a object, pass its token to the TAP method, and then call the source’s method when you're ready to cancel the operation: + +```csharp +var cts = new CancellationTokenSource(); +string result = await DownloadStringAsync(url, cts.Token); +… // at some point later, potentially on another thread +cts.Cancel(); +``` + + To cancel multiple asynchronous invocations, you can pass the same token to all invocations: + +```csharp +var cts = new CancellationTokenSource(); + IList results = await Task.WhenAll(from url in urls select DownloadStringAsync(url, cts.Token)); + // at some point later, potentially on another thread + … + cts.Cancel(); +``` + + Or, you can pass the same token to a selective subset of operations: + +```csharp +var cts = new CancellationTokenSource(); + byte [] data = await DownloadDataAsync(url, cts.Token); + await SaveToDiskAsync(outputPath, data, CancellationToken.None); + … // at some point later, potentially on another thread + cts.Cancel(); +``` + + Cancellation requests may be initiated from any thread. + + You can pass the value to any method that accepts a cancellation token to indicate that cancellation will never be requested. This causes the property to return `false`, and the called method can optimize accordingly. For testing purposes, you can also pass in a pre-canceled cancellation token that is instantiated by using the constructor that accepts a Boolean value to indicate whether the token should start in an already-canceled or not-cancelable state. + + This approach to cancellation has several advantages: + +- You can pass the same cancellation token to any number of asynchronous and synchronous operations. + +- The same cancellation request may be proliferated to any number of listeners. + +- The developer of the asynchronous API is in complete control of whether cancellation may be requested and when it may take effect. + +- The code that consumes the API may selectively determine the asynchronous invocations that cancellation requests will be propagated to. + +## Monitoring Progress + Some asynchronous methods expose progress through a progress interface passed into the asynchronous method. For example, consider a function which asynchronously downloads a string of text, and along the way raises progress updates that include the percentage of the download that has completed thus far. Such a method could be consumed in a Windows Presentation Foundation (WPF) application as follows: + +```csharp +private async void btnDownload_Click(object sender, RoutedEventArgs e) +{ + btnDownload.IsEnabled = false; + try + { + txtResult.Text = await DownloadStringAsync(txtUrl.Text, + new Progress(p => pbDownloadProgress.Value = p)); + } + finally { btnDownload.IsEnabled = true; } +} +``` + + +## Using the Built-in Task-based Combinators + The namespace includes several methods for composing and working with tasks. + +### Task.Run + The class includes several methods that let you easily offload work as a or to the thread pool, for example: + +```csharp +public async void button1_Click(object sender, EventArgs e) +{ + textBox1.Text = await Task.Run(() => + { + // … do compute-bound work here + return answer; + }); +} +``` + + Some of these methods, such as the overload, exist as shorthand for the method. Other overloads, such as , enable you to use await within the offloaded work, for example: + +```csharp +public async void button1_Click(object sender, EventArgs e) +{ + pictureBox1.Image = await Task.Run(async() => + { + using(Bitmap bmp1 = await DownloadFirstImageAsync()) + using(Bitmap bmp2 = await DownloadSecondImageAsync()) + return Mashup(bmp1, bmp2); + }); +} +``` + + Such overloads are logically equivalent to using the method in conjunction with the extension method in the Task Parallel Library. + +### Task.FromResult + Use the method in scenarios where data may already be available and just needs to be returned from a task-returning method lifted into a : + +```csharp +public Task GetValueAsync(string key) +{ + int cachedValue; + return TryGetCachedValue(out cachedValue) ? + Task.FromResult(cachedValue) : + GetValueAsyncInternal(); +} + +private async Task GetValueAsyncInternal(string key) +{ + … +} +``` + +### Task.WhenAll + Use the method to asynchronously wait on multiple asynchronous operations that are represented as tasks. The method has multiple overloads that support a set of non-generic tasks or a non-uniform set of generic tasks (for example, asynchronously waiting for multiple void-returning operations, or asynchronously waiting for multiple value-returning methods where each value may have a different type) and to support a uniform set of generic tasks (such as asynchronously waiting for multiple `TResult`-returning methods). + + Let's say you want to send email messages to several customers. You can overlap sending the messages so you're not waiting for one message to complete before sending the next. You can also find out when the send operations have completed and whether any errors have occurred: + +```csharp +IEnumerable asyncOps = from addr in addrs select SendMailAsync(addr); +await Task.WhenAll(asyncOps); +``` + + This code doesn't explicitly handle exceptions that may occur, but lets exceptions propagate out of the `await` on the resulting task from . To handle the exceptions, you can use code such as the following: + +```csharp +IEnumerable asyncOps = from addr in addrs select SendMailAsync(addr); +try +{ + await Task.WhenAll(asyncOps); +} +catch(Exception exc) +{ + ... +} +``` + + In this case, if any asynchronous operation fails, all the exceptions will be consolidated in an exception, which is stored in the that is returned from the method. However, only one of those exceptions is propagated by the `await` keyword. If you want to examine all the exceptions, you can rewrite the previous code as follows: + +```csharp +Task [] asyncOps = (from addr in addrs select SendMailAsync(addr)).ToArray(); +try +{ + await Task.WhenAll(asyncOps); +} +catch(Exception exc) +{ + foreach(Task faulted in asyncOps.Where(t => t.IsFaulted)) + { + … // work with faulted and faulted.Exception + } +} +``` + + Let's consider an example of downloading multiple files from the web asynchronously. In this case, all the asynchronous operations have homogeneous result types, and it's easy to access the results: + +```csharp +string [] pages = await Task.WhenAll( + from url in urls select DownloadStringAsync(url)); +``` + + You can use the same exception-handling techniques we discussed in the previous void-returning scenario: + +```csharp +Task [] asyncOps = + (from url in urls select DownloadStringAsync(url)).ToArray(); +try +{ + string [] pages = await Task.WhenAll(asyncOps); + ... +} +catch(Exception exc) +{ + foreach(Task faulted in asyncOps.Where(t => t.IsFaulted)) + { + … // work with faulted and faulted.Exception + } +} +``` + +### Task.WhenAny + You can use the method to asynchronously wait for just one of multiple asynchronous operations represented as tasks to complete. This method serves four primary use cases: + +- Redundancy: Performing an operation multiple times and selecting the one that completes first (for example, contacting multiple stock quote web services that will produce a single result and selecting the one that completes the fastest). + +- Interleaving: Launching multiple operations and waiting for all of them to complete, but processing them as they complete. + +- Throttling: Allowing additional operations to begin as others complete. This is an extension of the interleaving scenario. + +- Early bailout: For example, an operation represented by task t1 can be grouped in a task with another task t2, and you can wait on the task. Task t2 could represent a time-out, or cancellation, or some other signal that causes the task to complete before t1 completes. + +#### Redundancy + Consider a case where you want to make a decision about whether to buy a stock. There are several stock recommendation web services that you trust, but depending on daily load, each service can end up being slow at different times. You can use the method to receive a notification when any operation completes: + +```csharp +var recommendations = new List>() +{ + GetBuyRecommendation1Async(symbol), + GetBuyRecommendation2Async(symbol), + GetBuyRecommendation3Async(symbol) +}; +Task recommendation = await Task.WhenAny(recommendations); +if (await recommendation) BuyStock(symbol); +``` + + Unlike , which returns the unwrapped results of all tasks that completed successfully, returns the task that completed. If a task fails, it’s important to know that it failed, and if a task succeeds, it’s important to know which task the return value is associated with. Therefore, you need to access the result of the returned task, or further await it, as this example shows. + + As with , you have to be able to accommodate exceptions. Because you receive the completed task back, you can await the returned task to have errors propagated, and `try/catch` them appropriately; for example: + +```csharp +Task [] recommendations = …; +while(recommendations.Count > 0) +{ + Task recommendation = await Task.WhenAny(recommendations); + try + { + if (await recommendation) BuyStock(symbol); + break; + } + catch(WebException exc) + { + recommendations.Remove(recommendation); + } +} +``` + + Additionally, even if a first task completes successfully, subsequent tasks may fail. At this point, you have several options for dealing with exceptions: You can wait until all the launched tasks have completed, in which case you can use the method, or you can decide that all exceptions are important and must be logged. For this, you can use continuations to receive a notification when tasks have completed asynchronously: + +```csharp +foreach(Task recommendation in recommendations) +{ + var ignored = recommendation.ContinueWith( + t => { if (t.IsFaulted) Log(t.Exception); }); +} +``` + + or: + +```csharp +foreach(Task recommendation in recommendations) +{ + var ignored = recommendation.ContinueWith( + t => Log(t.Exception), TaskContinuationOptions.OnlyOnFaulted); +} +``` + + or even: + +```csharp +private static async void LogCompletionIfFailed(IEnumerable tasks) +{ + foreach(var task in tasks) + { + try { await task; } + catch(Exception exc) { Log(exc); } + } +} +… +LogCompletionIfFailed(recommendations); +``` + + Finally, you may want to cancel all the remaining operations: + +```csharp +var cts = new CancellationTokenSource(); +var recommendations = new List>() +{ + GetBuyRecommendation1Async(symbol, cts.Token), + GetBuyRecommendation2Async(symbol, cts.Token), + GetBuyRecommendation3Async(symbol, cts.Token) +}; + +Task recommendation = await Task.WhenAny(recommendations); +cts.Cancel(); +if (await recommendation) BuyStock(symbol); +``` + +#### Interleaving + Consider a case where you're downloading images from the web and processing each image (for example, adding the image to a UI control). You have to do the processing sequentially on the UI thread, but you want to download the images as concurrently as possible. Also, you don’t want to hold up adding the images to the UI until they’re all downloaded—you want to add them as they complete: + +```csharp +List> imageTasks = + (from imageUrl in urls select GetBitmapAsync(imageUrl)).ToList(); +while(imageTasks.Count > 0) +{ + try + { + Task imageTask = await Task.WhenAny(imageTasks); + imageTasks.Remove(imageTask); + + Bitmap image = await imageTask; + panel.AddImage(image); + } + catch{} +} +``` + + You can also apply interleaving to a scenario that involves computationally intensive processing on the of the downloaded images; for example: + +```csharp +List> imageTasks = + (from imageUrl in urls select GetBitmapAsync(imageUrl) + .ContinueWith(t => ConvertImage(t.Result)).ToList(); +while(imageTasks.Count > 0) +{ + try + { + Task imageTask = await Task.WhenAny(imageTasks); + imageTasks.Remove(imageTask); + + Bitmap image = await imageTask; + panel.AddImage(image); + } + catch{} +} +``` + +#### Throttling + Consider the interleaving example, except that the user is downloading so many images that the downloads have to be throttled; for example, you want only a specific number of downloads to happen concurrently. To achieve this, you can start a subset of the asynchronous operations. As operations complete, you can start additional operations to take their place: + +```csharp +const int CONCURRENCY_LEVEL = 15; +Uri [] urls = …; +int nextIndex = 0; +var imageTasks = new List>(); +while(nextIndex < CONCURRENCY_LEVEL && nextIndex < urls.Length) +{ + imageTasks.Add(GetBitmapAsync(urls[nextIndex])); + nextIndex++; +} + +while(imageTasks.Count > 0) +{ + try + { + Task imageTask = await Task.WhenAny(imageTasks); + imageTasks.Remove(imageTask); + + Bitmap image = await imageTask; + panel.AddImage(image); + } + catch(Exception exc) { Log(exc); } + + if (nextIndex < urls.Length) + { + imageTasks.Add(GetBitmapAsync(urls[nextIndex])); + nextIndex++; + } +} +``` + +#### Early Bailout + Consider that you're waiting asynchronously for an operation to complete while simultaneously responding to a user’s cancellation request (for example, the user clicked a cancel button). The following code illustrates this scenario: + +```csharp +private CancellationTokenSource m_cts; + +public void btnCancel_Click(object sender, EventArgs e) +{ + if (m_cts != null) m_cts.Cancel(); +} + +public async void btnRun_Click(object sender, EventArgs e) +{ + m_cts = new CancellationTokenSource(); + btnRun.Enabled = false; + try + { + Task imageDownload = GetBitmapAsync(txtUrl.Text); + await UntilCompletionOrCancellation(imageDownload, m_cts.Token); + if (imageDownload.IsCompleted) + { + Bitmap image = await imageDownload; + panel.AddImage(image); + } + else imageDownload.ContinueWith(t => Log(t)); + } + finally { btnRun.Enabled = true; } +} + +private static async Task UntilCompletionOrCancellation( + Task asyncOp, CancellationToken ct) +{ + var tcs = new TaskCompletionSource(); + using(ct.Register(() => tcs.TrySetResult(true))) + await Task.WhenAny(asyncOp, tcs.Task); + return asyncOp; +} +``` + + This implementation re-enables the user interface as soon as you decide to bail out, but doesn't cancel the underlying asynchronous operations. Another alternative would be to cancel the pending operations when you decide to bail out, but not reestablish the user interface until the operations actually complete, potentially due to ending early due to the cancellation request: + +```csharp +private CancellationTokenSource m_cts; + +public async void btnRun_Click(object sender, EventArgs e) +{ + m_cts = new CancellationTokenSource(); + + btnRun.Enabled = false; + try + { + Task imageDownload = GetBitmapAsync(txtUrl.Text, m_cts.Token); + await UntilCompletionOrCancellation(imageDownload, m_cts.Token); + Bitmap image = await imageDownload; + panel.AddImage(image); + } + catch(OperationCanceledException) {} + finally { btnRun.Enabled = true; } +} +``` + + Another example of early bailout involves using the method in conjunction with the method, as discussed in the next section. + +### Task.Delay + You can use the method to introduce pauses into an asynchronous method’s execution. This is useful for many kinds of functionality, including building polling loops and delaying the handling of user input for a predetermined period of time. The method can also be useful in combination with for implementing time-outs on awaits. + + If a task that’s part of a larger asynchronous operation (for example, an ASP.NET web service) takes too long to complete, the overall operation could suffer, especially if it fails to ever complete. For this reason, it’s important to be able to time out when waiting on an asynchronous operation. The synchronous , , and methods accept time-out values, but the corresponding / and the previously mentioned / methods do not. Instead, you can use and in combination to implement a time-out. + + For example, in your UI application, let's say that you want to download an image and disable the UI while the image is downloading. However, if the download takes too long, you want to re-enable the UI and discard the download: + +```csharp +public async void btnDownload_Click(object sender, EventArgs e) +{ + btnDownload.Enabled = false; + try + { + Task download = GetBitmapAsync(url); + if (download == await Task.WhenAny(download, Task.Delay(3000))) + { + Bitmap bmp = await download; + pictureBox.Image = bmp; + status.Text = "Downloaded"; + } + else + { + pictureBox.Image = null; + status.Text = "Timed out"; + var ignored = download.ContinueWith( + t => Trace("Task finally completed")); + } + } + finally { btnDownload.Enabled = true; } +} +``` + + The same applies to multiple downloads, because returns a task: + +```csharp +public async void btnDownload_Click(object sender, RoutedEventArgs e) +{ + btnDownload.Enabled = false; + try + { + Task downloads = + Task.WhenAll(from url in urls select GetBitmapAsync(url)); + if (downloads == await Task.WhenAny(downloads, Task.Delay(3000))) + { + foreach(var bmp in downloads) panel.AddImage(bmp); + status.Text = "Downloaded"; + } + else + { + status.Text = "Timed out"; + downloads.ContinueWith(t => Log(t)); + } + } + finally { btnDownload.Enabled = true; } +} +``` + +## Building Task-based Combinators + Because a task is able to completely represent an asynchronous operation and provide synchronous and asynchronous capabilities for joining with the operation, retrieving its results, and so on, you can build useful libraries of combinators that compose tasks to build larger patterns. As discussed in the previous section, the .NET Framework includes several built-in combinators, but you can also build your own. The following sections provide several examples of potential combinator methods and types. + +### RetryOnFault + In many situations, you may want to retry an operation if a previous attempt fails. For synchronous code, you might build a helper method such as `RetryOnFault` in the following example to accomplish this: + +```csharp +public static T RetryOnFault( + Func function, int maxTries) +{ + for(int i=0; i RetryOnFault( + Func> function, int maxTries) +{ + for(int i=0; i DownloadStringAsync(url), 3); +``` + + You could extend the `RetryOnFault` function further. For example, the function could accept another `Func` that will be invoked between retries to determine when to try the operation again; for example: + +```csharp +public static async Task RetryOnFault( + Func> function, int maxTries, Func retryWhen) +{ + for(int i=0; i DownloadStringAsync(url), 3, () => Task.Delay(1000)); +``` + +### NeedOnlyOne + Sometimes, you can take advantage of redundancy to improve an operation’s latency and chances for success. Consider multiple web services that provide stock quotes, but at various times of the day, each service may provide different levels of quality and response times. To deal with these fluctuations, you may issue requests to all the web services, and as soon as you get a response from one, cancel the remaining requests. You can implement a helper function to make it easier to implement this common pattern of launching multiple operations, waiting for any, and then canceling the rest. The `NeedOnlyOne` function in the following example illustrates this scenario: + +```csharp +public static async Task NeedOnlyOne( + params Func> [] functions) +{ + var cts = new CancellationTokenSource(); + var tasks = (from function in functions + select function(cts.Token)).ToArray(); + var completed = await Task.WhenAny(tasks).ConfigureAwait(false); + cts.Cancel(); + foreach(var task in tasks) + { + var ignored = task.ContinueWith( + t => Log(t), TaskContinuationOptions.OnlyOnFaulted); + } + return completed; +} +``` + + You can then use this function as follows: + +```csharp +double currentPrice = await NeedOnlyOne( + ct => GetCurrentPriceFromServer1Async("msft", ct), + ct => GetCurrentPriceFromServer2Async("msft", ct), + ct => GetCurrentPriceFromServer3Async("msft", ct)); +``` + +### Interleaved Operations + There is a potential performance problem with using the method to support an interleaving scenario when you're working with very large sets of tasks. Every call to results in a continuation being registered with each task. For N number of tasks, this results in O(N2) continuations created over the lifetime of the interleaving operation. If you're working with a large set of tasks, you can use a combinator (`Interleaved` in the following example) to address the performance issue: + +```csharp +static IEnumerable> Interleaved(IEnumerable> tasks) +{ + var inputTasks = tasks.ToList(); + var sources = (from _ in Enumerable.Range(0, inputTasks.Count) + select new TaskCompletionSource()).ToList(); + int nextTaskIndex = -1; + foreach (var inputTask in inputTasks) + { + inputTask.ContinueWith(completed => + { + var source = sources[Interlocked.Increment(ref nextTaskIndex)]; + if (completed.IsFaulted) + source.TrySetException(completed.Exception.InnerExceptions); + else if (completed.IsCanceled) + source.TrySetCanceled(); + else + source.TrySetResult(completed.Result); + }, CancellationToken.None, + TaskContinuationOptions.ExecuteSynchronously, + TaskScheduler.Default); + } + return from source in sources + select source.Task; +} +``` + + You can then use the combinator to process the results of tasks as they complete; for example: + +```csharp +IEnumerable> tasks = ...; +foreach(var task in Interleaved(tasks)) +{ + int result = await task; + … +} +``` + +### WhenAllOrFirstException + In certain scatter/gather scenarios, you might want to wait for all tasks in a set, unless one of them faults, in which case you want to stop waiting as soon as the exception occurs. You can accomplish that with a combinator method such as `WhenAllOrFirstException` in the following example: + +```csharp +public static Task WhenAllOrFirstException(IEnumerable> tasks) +{ + var inputs = tasks.ToList(); + var ce = new CountdownEvent(inputs.Count); + var tcs = new TaskCompletionSource(); + + Action onCompleted = (Task completed) => + { + if (completed.IsFaulted) + tcs.TrySetException(completed.Exception.InnerExceptions); + if (ce.Signal() && !tcs.Task.IsCompleted) + tcs.TrySetResult(inputs.Select(t => t.Result).ToArray()); + }; + + foreach (var t in inputs) t.ContinueWith(onCompleted); + return tcs.Task; +} +``` + +## Building Task-based Data Structures + In addition to the ability to build custom task-based combinators, having a data structure in and that represents both the results of an asynchronous operation and the necessary synchronization to join with it makes it a very powerful type on which to build custom data structures to be used in asynchronous scenarios. + +### AsyncCache + One important aspect of a task is that it may be handed out to multiple consumers, all of whom may await it, register continuations with it, get its result or exceptions (in the case of ), and so on. This makes and perfectly suited to be used in an asynchronous caching infrastructure. Here’s an example of a small but powerful asynchronous cache built on top of : + +```csharp +public class AsyncCache +{ + private readonly Func> _valueFactory; + private readonly ConcurrentDictionary>> _map; + + public AsyncCache(Func> valueFactory) + { + if (valueFactory == null) throw new ArgumentNullException("loader"); + _valueFactory = valueFactory; + _map = new ConcurrentDictionary>>(); + } + + public Task this[TKey key] + { + get + { + if (key == null) throw new ArgumentNullException("key"); + return _map.GetOrAdd(key, toAdd => + new Lazy>(() => _valueFactory(toAdd))).Value; + } + } +} +``` + + The [AsyncCache\](https://blogs.msdn.microsoft.com/pfxteam/2010/04/23/parallelextensionsextras-tour-12-asynccache/) class accepts as a delegate to its constructor a function that takes a `TKey` and returns a . Any previously accessed values from the cache are stored in the internal dictionary, and the `AsyncCache` ensures that only one task is generated per key, even if the cache is accessed concurrently. + + For example, you can build a cache for downloaded web pages: + +```csharp +private AsyncCache m_webPages = + new AsyncCache(DownloadStringAsync); +``` + + You can then use this cache in asynchronous methods whenever you need the contents of a web page. The `AsyncCache` class ensures that you’re downloading as few pages as possible, and caches the results. + +```csharp +private async void btnDownload_Click(object sender, RoutedEventArgs e) +{ + btnDownload.IsEnabled = false; + try + { + txtContents.Text = await m_webPages["http://www.microsoft.com"]; + } + finally { btnDownload.IsEnabled = true; } +} +``` + +### AsyncProducerConsumerCollection + You can also use tasks to build data structures for coordinating asynchronous activities. Consider one of the classic parallel design patterns: producer/consumer. In this pattern, producers generate data that is consumed by consumers, and the producers and consumers may run in parallel. For example, the consumer processes item 1, which was previously generated by a producer who is now producing item 2. For the producer/consumer pattern, you invariably need some data structure to store the work created by producers so that the consumers may be notified of new data and find it when available. + + Here’s a simple data structure built on top of tasks that enables asynchronous methods to be used as producers and consumers: + +```csharp +public class AsyncProducerConsumerCollection +{ + private readonly Queue m_collection = new Queue(); + private readonly Queue> m_waiting = + new Queue>(); + + public void Add(T item) + { + TaskCompletionSource tcs = null; + lock (m_collection) + { + if (m_waiting.Count > 0) tcs = m_waiting.Dequeue(); + else m_collection.Enqueue(item); + } + if (tcs != null) tcs.TrySetResult(item); + } + + public Task Take() + { + lock (m_collection) + { + if (m_collection.Count > 0) + { + return Task.FromResult(m_collection.Dequeue()); + } + else + { + var tcs = new TaskCompletionSource(); + m_waiting.Enqueue(tcs); + return tcs.Task; + } + } + } +} +``` + + With that data structure in place, you can write code such as the following: + +```csharp +private static AsyncProducerConsumerCollection m_data = …; +… +private static async Task ConsumerAsync() +{ + while(true) + { + int nextItem = await m_data.Take(); + ProcessNextItem(nextItem); + } +} +… +private static void Produce(int data) +{ + m_data.Add(data); +} +``` + +The namespace includes the type, which you can use in a similar manner, but without having to build a custom collection type: + +```csharp +private static BufferBlock m_data = …; +… +private static async Task ConsumerAsync() +{ + while(true) + { + int nextItem = await m_data.ReceiveAsync(); + ProcessNextItem(nextItem); + } +} +… +private static void Produce(int data) +{ + m_data.Post(data); +} +``` + > [!NOTE] -> The namespace is available in the [!INCLUDE[net_v45](../../../includes/net-v45-md.md)] through **NuGet**. To install the assembly that contains the namespace, open your project in [!INCLUDE[vs_dev11_long](../../../includes/vs-dev11-long-md.md)], choose **Manage NuGet Packages** from the Project menu, and search online for the Microsoft.Tpl.Dataflow package. - +> The namespace is available in the [!INCLUDE[net_v45](../../../includes/net-v45-md.md)] through **NuGet**. To install the assembly that contains the namespace, open your project in Visual Studio, choose **Manage NuGet Packages** from the Project menu, and search online for the Microsoft.Tpl.Dataflow package. + ## See also -- [Task-based Asynchronous Pattern (TAP)](../../../docs/standard/asynchronous-programming-patterns/task-based-asynchronous-pattern-tap.md) -- [Implementing the Task-based Asynchronous Pattern](../../../docs/standard/asynchronous-programming-patterns/implementing-the-task-based-asynchronous-pattern.md) +- [Task-based Asynchronous Pattern (TAP)](../../../docs/standard/asynchronous-programming-patterns/task-based-asynchronous-pattern-tap.md) +- [Implementing the Task-based Asynchronous Pattern](../../../docs/standard/asynchronous-programming-patterns/implementing-the-task-based-asynchronous-pattern.md) - [Interop with Other Asynchronous Patterns and Types](../../../docs/standard/asynchronous-programming-patterns/interop-with-other-asynchronous-patterns-and-types.md) diff --git a/docs/standard/io/asynchronous-file-i-o.md b/docs/standard/io/asynchronous-file-i-o.md index 80087b3faf332..15d613c88249a 100644 --- a/docs/standard/io/asynchronous-file-i-o.md +++ b/docs/standard/io/asynchronous-file-i-o.md @@ -2,10 +2,10 @@ title: "Asynchronous File I/O" ms.date: "03/30/2017" ms.technology: dotnet-standard -dev_langs: +dev_langs: - "csharp" - "vb" -helpviewer_keywords: +helpviewer_keywords: - "streams, synchronous streams" - "asynchronous I/O" - "synchronous I/O" @@ -21,39 +21,40 @@ author: "mairaw" ms.author: "mairaw" --- # Asynchronous File I/O -Asynchronous operations enable you to perform resource-intensive I/O operations without blocking the main thread. This performance consideration is particularly important in a [!INCLUDE[win8_appname_long](../../../includes/win8-appname-long-md.md)] app or [!INCLUDE[desktop_appname](../../../includes/desktop-appname-md.md)] app where a time-consuming stream operation can block the UI thread and make your app appear as if it is not working. - - Starting with the [!INCLUDE[net_v45](../../../includes/net-v45-md.md)], the I/O types include async methods to simplify asynchronous operations. An async method contains `Async` in its name, such as , , , , , and . These async methods are implemented on stream classes, such as , , and , and on classes that are used for reading from or writing to streams, such and . - - In the .NET Framework 4 and earlier versions, you have to use methods such as and to implement asynchronous I/O operations. These methods are still available in the [!INCLUDE[net_v45](../../../includes/net-v45-md.md)] to support legacy code; however, the async methods help you implement asynchronous I/O operations more easily. - - Starting with [!INCLUDE[vs_dev11_long](../../../includes/vs-dev11-long-md.md)], Visual Studio provides two keywords for asynchronous programming: - - `Async` (Visual Basic) or `async` (C#) modifier, which is used to mark a method that contains an asynchronous operation. - - `Await` (Visual Basic) or `await` (C#) operator, which is applied to the result of an async method. - - To implement asynchronous I/O operations, use these keywords in conjunction with the async methods, as shown in the following examples. For more information, see [Asynchronous Programming with Async and Await](https://msdn.microsoft.com/library/db854f91-ccef-4035-ae4d-0911fde808c7). - - The following example demonstrates how to use two objects to copy files asynchronously from one directory to another. Notice that the event handler for the control is marked with the `async` modifier because it calls an asynchronous method. - - [!code-csharp[Asynchronous_File_IO_async#1](../../../samples/snippets/csharp/VS_Snippets_CLR/Asynchronous_File_IO_async/cs/example.cs#1)] - [!code-vb[Asynchronous_File_IO_async#1](../../../samples/snippets/visualbasic/VS_Snippets_CLR/Asynchronous_File_IO_async/vb/example.vb#1)] - - The next example is similar to the previous one but uses and objects to read and write the contents of a text file asynchronously. - - [!code-csharp[Asynchronous_File_IO_async#2](../../../samples/snippets/csharp/VS_Snippets_CLR/Asynchronous_File_IO_async/cs/example2.cs#2)] - [!code-vb[Asynchronous_File_IO_async#2](../../../samples/snippets/visualbasic/VS_Snippets_CLR/Asynchronous_File_IO_async/vb/example2.vb#2)] - - The next example shows the code-behind file and the XAML file that are used to open a file as a in a [!INCLUDE[win8_appname_long](../../../includes/win8-appname-long-md.md)] app, and read its contents by using an instance of the class. It uses asynchronous methods to open the file as a stream and to read its contents. - - [!code-csharp[System.IO.WindowsRuntimeStorageExtensions#2](../../../samples/snippets/csharp/VS_Snippets_CLR_System/system.io.windowsruntimestorageextensions/cs/blankpage.xaml.cs#2)] - [!code-vb[System.IO.WindowsRuntimeStorageExtensions#2](../../../samples/snippets/visualbasic/VS_Snippets_CLR_System/system.io.windowsruntimestorageextensions/vb/blankpage.xaml.vb#2)] - - [!code-xaml[System.IO.WindowsRuntimeStorageExtensions#1](../../../samples/snippets/csharp/VS_Snippets_CLR_System/system.io.windowsruntimestorageextensions/cs/blankpage.xaml#1)] - + +Asynchronous operations enable you to perform resource-intensive I/O operations without blocking the main thread. This performance consideration is particularly important in a [!INCLUDE[win8_appname_long](../../../includes/win8-appname-long-md.md)] app or [!INCLUDE[desktop_appname](../../../includes/desktop-appname-md.md)] app where a time-consuming stream operation can block the UI thread and make your app appear as if it is not working. + +Starting with the [!INCLUDE[net_v45](../../../includes/net-v45-md.md)], the I/O types include async methods to simplify asynchronous operations. An async method contains `Async` in its name, such as , , , , , and . These async methods are implemented on stream classes, such as , , and , and on classes that are used for reading from or writing to streams, such and . + +In the .NET Framework 4 and earlier versions, you have to use methods such as and to implement asynchronous I/O operations. These methods are still available in the [!INCLUDE[net_v45](../../../includes/net-v45-md.md)] to support legacy code; however, the async methods help you implement asynchronous I/O operations more easily. + +C# and Visual Basic each have two keywords for asynchronous programming: + +- `Async` (Visual Basic) or `async` (C#) modifier, which is used to mark a method that contains an asynchronous operation. + +- `Await` (Visual Basic) or `await` (C#) operator, which is applied to the result of an async method. + +To implement asynchronous I/O operations, use these keywords in conjunction with the async methods, as shown in the following examples. For more information, see [Asynchronous Programming with Async and Await](https://msdn.microsoft.com/library/db854f91-ccef-4035-ae4d-0911fde808c7). + +The following example demonstrates how to use two objects to copy files asynchronously from one directory to another. Notice that the event handler for the control is marked with the `async` modifier because it calls an asynchronous method. + +[!code-csharp[Asynchronous_File_IO_async#1](../../../samples/snippets/csharp/VS_Snippets_CLR/Asynchronous_File_IO_async/cs/example.cs#1)] +[!code-vb[Asynchronous_File_IO_async#1](../../../samples/snippets/visualbasic/VS_Snippets_CLR/Asynchronous_File_IO_async/vb/example.vb#1)] + +The next example is similar to the previous one but uses and objects to read and write the contents of a text file asynchronously. + +[!code-csharp[Asynchronous_File_IO_async#2](../../../samples/snippets/csharp/VS_Snippets_CLR/Asynchronous_File_IO_async/cs/example2.cs#2)] +[!code-vb[Asynchronous_File_IO_async#2](../../../samples/snippets/visualbasic/VS_Snippets_CLR/Asynchronous_File_IO_async/vb/example2.vb#2)] + +The next example shows the code-behind file and the XAML file that are used to open a file as a in a [!INCLUDE[win8_appname_long](../../../includes/win8-appname-long-md.md)] app, and read its contents by using an instance of the class. It uses asynchronous methods to open the file as a stream and to read its contents. + +[!code-csharp[System.IO.WindowsRuntimeStorageExtensions#2](../../../samples/snippets/csharp/VS_Snippets_CLR_System/system.io.windowsruntimestorageextensions/cs/blankpage.xaml.cs#2)] +[!code-vb[System.IO.WindowsRuntimeStorageExtensions#2](../../../samples/snippets/visualbasic/VS_Snippets_CLR_System/system.io.windowsruntimestorageextensions/vb/blankpage.xaml.vb#2)] + +[!code-xaml[System.IO.WindowsRuntimeStorageExtensions#1](../../../samples/snippets/csharp/VS_Snippets_CLR_System/system.io.windowsruntimestorageextensions/cs/blankpage.xaml#1)] + ## See also -- -- [File and Stream I/O](../../../docs/standard/io/index.md) +- +- [File and Stream I/O](../../../docs/standard/io/index.md) - [Asynchronous Programming with Async and Await](https://msdn.microsoft.com/library/db854f91-ccef-4035-ae4d-0911fde808c7) diff --git a/docs/standard/parallel-programming/how-to-use-parallel-invoke-to-execute-parallel-operations.md b/docs/standard/parallel-programming/how-to-use-parallel-invoke-to-execute-parallel-operations.md index 0230c3b6c4a9b..ab60b6a77acbe 100644 --- a/docs/standard/parallel-programming/how-to-use-parallel-invoke-to-execute-parallel-operations.md +++ b/docs/standard/parallel-programming/how-to-use-parallel-invoke-to-execute-parallel-operations.md @@ -2,10 +2,10 @@ title: "How to: Use Parallel.Invoke to Execute Parallel Operations" ms.date: "03/30/2017" ms.technology: dotnet-standard -dev_langs: +dev_langs: - "csharp" - "vb" -helpviewer_keywords: +helpviewer_keywords: - "task parallelism in .NET" - "parallel programming, task parallelism" ms.assetid: 6b3ecd79-dec9-4ce1-abf4-62e5392a59c6 @@ -13,25 +13,27 @@ author: "rpetrusha" ms.author: "ronpet" --- # How to: Use Parallel.Invoke to Execute Parallel Operations -This example shows how to parallelize operations by using in the Task Parallel Library. Three operations are performed on a shared data source. Because none of the operations modifies the source, they can be executed in parallel in a straightforward manner. - + +This example shows how to parallelize operations by using in the Task Parallel Library. Three operations are performed on a shared data source. Because none of the operations modifies the source, they can be executed in parallel in a straightforward manner. + > [!NOTE] -> This documentation uses lambda expressions to define delegates in TPL. If you are not familiar with lambda expressions in C# or Visual Basic, see [Lambda Expressions in PLINQ and TPL](../../../docs/standard/parallel-programming/lambda-expressions-in-plinq-and-tpl.md). - -## Example - [!code-csharp[TPL_Parallel#06](../../../samples/snippets/csharp/VS_Snippets_Misc/tpl_parallel/cs/parallelinvoke.cs#06)] - [!code-vb[TPL_Parallel#06](../../../samples/snippets/visualbasic/VS_Snippets_Misc/tpl_parallel/vb/parallelinvoke.vb#06)] - - Note that with , you simply express which actions you want to run concurrently, and the runtime handles all thread scheduling details, including scaling automatically to the number of cores on the host computer. - - This example parallelizes the operations, not the data. As an alternate approach, you can parallelize the LINQ queries by using PLINQ and run the queries sequentially. Alternatively, you could parallelize the data by using PLINQ. Another option is to parallelize both the queries and the tasks. Although the resulting overhead might degrade performance on host computers with relatively few processors, it would scale much better on computers with many processors. - -## Compiling the Code - -- Copy and paste the entire example into a Microsoft Visual Studio 2010 project and press F5. - +> This documentation uses lambda expressions to define delegates in TPL. If you are not familiar with lambda expressions in C# or Visual Basic, see [Lambda Expressions in PLINQ and TPL](../../../docs/standard/parallel-programming/lambda-expressions-in-plinq-and-tpl.md). + +## Example + +[!code-csharp[TPL_Parallel#06](../../../samples/snippets/csharp/VS_Snippets_Misc/tpl_parallel/cs/parallelinvoke.cs#06)] +[!code-vb[TPL_Parallel#06](../../../samples/snippets/visualbasic/VS_Snippets_Misc/tpl_parallel/vb/parallelinvoke.vb#06)] + +Note that with , you simply express which actions you want to run concurrently, and the runtime handles all thread scheduling details, including scaling automatically to the number of cores on the host computer. + +This example parallelizes the operations, not the data. As an alternate approach, you can parallelize the LINQ queries by using PLINQ and run the queries sequentially. Alternatively, you could parallelize the data by using PLINQ. Another option is to parallelize both the queries and the tasks. Although the resulting overhead might degrade performance on host computers with relatively few processors, it would scale much better on computers with many processors. + +## Compile the Code + +Copy and paste the entire example into a Microsoft Visual Studio project and press **F5**. + ## See also -- [Parallel Programming](../../../docs/standard/parallel-programming/index.md) -- [How to: Cancel a Task and Its Children](../../../docs/standard/parallel-programming/how-to-cancel-a-task-and-its-children.md) +- [Parallel Programming](../../../docs/standard/parallel-programming/index.md) +- [How to: Cancel a Task and Its Children](../../../docs/standard/parallel-programming/how-to-cancel-a-task-and-its-children.md) - [Parallel LINQ (PLINQ)](../../../docs/standard/parallel-programming/parallel-linq-plinq.md)