ICode9

精准搜索请尝试: 精确搜索
首页 > 编程语言> 文章详细

C#异步函数_等待

2022-06-25 23:05:47  阅读:129  来源: 互联网

标签:异步 函数 C# await 1000000 int GetPrimesCountAsync Go


await关键字可以简便地附加延续。

static void Main(string[] args)
{
    DisplayPrimesCount();
}

static async void DisplayPrimesCount()
{
    int result = await GetPrimesCountAsync(2, 1000000);
    Console.WriteLine(result);
}

/// <summary>
/// 获得素数个数
/// </summary>
/// <param name="start">从什么数开始</param>
/// <param name="count">要获取的连续整数的数目</param>
/// <returns></returns>
static Task<int> GetPrimesCountAsync(int start, int count)
{
    return Task.Run(() =>ParallelEnumerable.Range(start, count)
        .Count(n => Enumerable.Range(2, (int)Math.Sqrt(n) - 1).All(i => n % i > 0)));
}

添加了async修饰符的方法称为异步函数。
当遇到await表达式时,通常情况下执行过程会返回到调用者上。运行时再返回之前会在等待的任务上附加一个延续,保证任务结束时执行点会跳回到方法中,并继续剩余的代码。如果顺利结束,则返回值为await表达式赋值。
可以以下例子展开形式印证:

static void DisplayPrimesCount()
{
    var awaiter = GetPrimesCountAsync(2, 1000000).GetAwaiter() ;
    awaiter.OnCompleted(()=>
    {
        int result = awaiter.GetResult();
        Console.WriteLine(result);
    });
}

await等待的表达式通常情况下是一个任务。但实际上,只要该对象拥有GetAwaiter方法,且该方法的返回值为等待器,则编译器都可以接受。

await表达式的最大优势在于它几乎可以出现在任何异步函数的表达式中,但不能出现在lock或unsafe上下文中。
以下示例中,await出现在循环结构中:

static async void DisplayPrimesCounts()
{
    for (int i = 0; i < 10; i++)
    {
        Console.WriteLine(await GetPrimesCountAsync(i * 1000000 + 2, 1000000));
    }
}

在第一次执行GetPrimesCountAsync方法时,由于出现了await表达式,因此执行点返回给调用者。当方法完成时,执行点会从停止之处恢复执行,同时保留局部变量和循环计数器的值。

UI上的等待处理

现在将编写一个UI程序,并且使该程序在调用计算密集的方法时仍然保持UI的响应性。
首先示例同步实现:

public partial class TestUI : Window
{
    Button _button = new Button { Content = "Go" };
    TextBlock _results = new TextBlock();
    public TestUI()
    {
        InitializeComponent();

        var panel = new StackPanel();
        panel.Children.Add(_button);
        panel.Children.Add(_results);
        Content = panel;
        _button.Click += (sender, args) => Go();
    }

    void Go()
    {
        for (int i = 1; i < 5; i++)
        {
            _results.Text += GetPrimesCount(i * 1000000, 1000000) +
               " primes between " + (i * 1000000) + " and " + ((i + 1) * 1000000 - 1) +
               Environment.NewLine;
        }

    }

    int GetPrimesCount(int start, int count)
    {
        return ParallelEnumerable.Range(start, count)
            .Count(n =>Enumerable.Range(2, (int)Math.Sqrt(n) - 1).All(i => n % i > 0));
    }
}

当点击按钮时,由于执行计算密集代码时间长,程序会陷入无响应状态。
我们可以分两步实现相应的异步方法。
第一步:异步的GetPrimesCount方法

Task<int> GetPrimesCountAsync (int start, int count)
{
	return Task.Run (() =>
		ParallelEnumerable.Range (start, count).Count (n => 
			Enumerable.Range (2, (int) Math.Sqrt(n)-1).All (i => n % i > 0)));
}

第二步:在Go方法中调用

async void Go()
{
	_button.IsEnabled = false;
	
	for (int i = 1; i < 5; i++)
		_results.Text += await GetPrimesCountAsync (i * 1000000, 1000000) +
			" primes between " + (i*1000000) + " and " + ((i+1)*1000000-1) + Environment.NewLine;

	_button.IsEnabled = true;
}

由以上代码可见异步函数的简洁性,只需按同步方式书写,并当调用异步函数时进行等待await就可以避免阻塞了。
GetPrimesCountAsync方法会运行在工作线程上,而Go方法则会“租用”UI线程时间,即Go方法在消息循环中是以伪并发的方式执行的。

标签:异步,函数,C#,await,1000000,int,GetPrimesCountAsync,Go
来源: https://www.cnblogs.com/nullcodeworld/p/16412642.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有