【问题标题】:async task inside loop in asp.net v4asp.net v4中循环内的异步任务
【发布时间】:2014-01-22 11:51:10
【问题描述】:

我正在使用 asp.net v4 c#,并且我有一个电子邮件地址列表。我希望我的一位管理员能够输入消息,按“发送”并让电子邮件一一发送。

我认为最好的方法是利用 .net 中的异步方法? OnClick 发送按钮,我获取电子邮件地址列表,然后调用异步方法。我只是不知道如何让它逐个遍历列表并触发电子邮件。

需要一次完成一个,以便我可以保存针对该用户帐户发送的电子邮件的副本。

这是我到目前为止所拥有的(从这里的教程/帖子拼凑而成)

protected void btnSend_Click(object sender, EventArgs e)
{
    //get a list of email addresses and send to them
    //these will come from the database once testing comp
    List<string> emails = new List<string>();
    emails.Add("test1@test.com");
    emails.Add("test2@test.com");
    emails.Add("test3@test.com");
    emails.Add("test4@test.com");
    emails.Add("test5@test.com");
    emails.Add("test6@test.com");
    emails.Add("test7@test.com");

    SendingDelegate worker = new SendingDelegate(DoSend);
    AsyncCallback completedCallback = new AsyncCallback(DoSendCompletedCallBack);

    lblThreadDetails.Text = "sending to " + emails.Count.ToString() + " email addresses";

    worker.BeginInvoke(completedCallback, AsyncOperationManager.CreateOperation(null));
    sending = true;

}

//boolean flag which indicates whether the async task is running
private bool sending = false;

private delegate bool SendingDelegate();

private bool DoSend()
{
    //send messages
    //emails sent here and saved in the DB each time

    //give the user some feed back on screen. X should be the email address.
    lblThreadDetails.Text = "processing " + x.ToString();
    Thread.Sleep(1000);
    return false;
}

private void DoSendCompletedCallBack(IAsyncResult ar)
{
    //get the original worker delegate and the AsyncOperation instance
    SendingDelegate worker = (SendingDelegate)((AsyncResult)ar).AsyncDelegate;

    //finish the asynchronous operation
    bool success = worker.EndInvoke(ar);
    sending = false;

    if (success)
    {
        //perform sql tasks now that crawl has completed
        lblThreadDetails.Text = "all done!";
    }
}

我基本上需要将异步函数的调用放入一个循环中,在那里我会遍历电子邮件地址列表。

这种方法有意义吗?

【问题讨论】:

  • Tangent:你知道你真的不需要new up 委托对象了吗?至少对我而言,这会使代码更具可读性,因为您不需要追溯到委托变量指向的位置。
  • 即你可以使用(DoSend as Action).BeginInvoke(DoSendCompletedCallback, AsyncOperationManager.CreateOperation(null))
  • 另外,有一个明显的问题不是您的方法,而是您的测试工具,即从后台操作更新 GUI。大多数 GUI 系统都是单线程的,即创建和更新 UI 必须在运行事件循环的线程上进行。
  • 我想做的是在这里使用Task.Run()async..await 功能,但我不熟悉它,没有时间准备和测试代码以获得正确的答案现在,所以你只能接受这个建议。
  • 我已经编辑了你的标题。请参阅“Should questions include “tags” in their titles?”,其中的共识是“不,他们不应该”。

标签: c# asp.net asynchronous async-await


【解决方案1】:

我认为您无法使用 ASP.NET 4.5 和 async/await,这可能是解决这种情况的理想解决方案(有关详细信息,请参阅 thisthis)。

但是,在 ASP 4.0 中,您仍然可以通过 PageAsyncTaskPage.RegisterAsyncTask 在您的页面上使用异步代码。这将延长请求的生命周期,直到异步任务完成:

PageAsyncTask asyncTask = new PageAsyncTask(
    slowTask.OnBegin, slowTask.OnEnd, slowTask1.OnTimeout, 
    "AsyncTaskName", true);

然后你从OnBegin/OnEnd 调用worker.BeginInvoke/EndInvoke。 MSDN 有一个complete sample code 这个。

解决问题本身:

这种方法有意义吗?感谢您提供任何信息。

如果我正确理解了您的代码,我认为它没有意义

显然,您在这里所做的是启动一个后台线程并在该线程上执行一些同步操作,例如您的DoSend() 中的Thread.Sleep(1000)。这对于客户端 UI 应用程序(以保持 UI 响应)是有意义的。

但是,对于您的服务器端应用程序,您并没有获得任何优势:至少有一个线程(以 BeginInvoke 开头的线程)在您正在进行的操作期间保持阻塞。

您不妨直接在处理 HTTP 请求的原始线程上(在btnSend_Click 内)调用DoSend(),而不使用BeginInvoke。在这种情况下,您的应用甚至可能会稍微更好地扩展,因为它的线程切换更少。

现在,也许您可​​以重构代码以使用不需要专用线程的纯异步 IO/网络绑定 API。这就是使您的应用程序扩展得更好的原因。阅读 Stephen ClearyThere Is No Thread 博客文章,以更好地了解我在说什么。

【讨论】:

    猜你喜欢
    • 2017-09-30
    • 1970-01-01
    • 2014-04-19
    • 2018-10-17
    • 1970-01-01
    • 1970-01-01
    • 2023-03-16
    • 2020-02-06
    • 2014-07-24
    相关资源
    最近更新 更多