10

我正在构建一个将所有内容存储在 Azure 表中的日志库。写入该表显然需要很多时间(从不超过 1 秒,但仍然太多让用户等待),所以 Log 方法返回一个 LogResult 实例,这里是类

public class LogResult
{
    public string Id { get; set; }
    public Task LoggingTask { get; set; }

    public LogResult(string id, Task task)
    {
        Id = id;
        LoggingTask = task;
    }
}

这是 Log 方法如何完成的

return new LogResult(id, Task.Factory.StartNew(() => 
    DoLogInAzure(account, id, exception, request))
);

为调用者提供等待它完成的选项(例如,如果它是控制台应用程序)。我面临的问题是 IIS在返回用户响应之前不应该等待它......如果我不等待它,IIS 并不总是执行任务。这个想法是向用户显示一条消息“......如果您与我们联系,请务必提及您的问题编号,XXX”,并且不要让他等到写入日志条目。

有没有办法强制 IIS 等到任务完成,即使它返回了响应?我想我可能需要编写一个异步接收请求的 Windows 服务,但看起来只是添加一个日志条目需要做很多工作......特别是如果我可以强制 IIS 等待它。

感谢您的任何想法!

4

4 回答 4

12

Phil Hack的这篇文章讨论了在 ASP.NET 应用程序中运行后台任务。

于 2012-09-30T23:03:10.463 回答
10

感谢 Damian Schenkelman 和 Phil Haack 的博客文章,我找到了问题和解决方案。问题是 IIS 在需要处理新请求时会重用线程。而且由于它不知道我的任务正在做一些工作,所以它重用了那个线程(这是有道理的)。然后,我只需要通知 IIS 我正在使用该线程并且它不能被重用(因此,它必须重用另一个线程,创建一个新线程,或者让它等待)。我最终使用了我自己的 TaskFactory 来处理任务创建,并在 IIS 中自动注册一个通知程序。为了完整起见,为了帮助与我有同样问题的其他人,并阅读其他建议,这就是我所做的

public class IISNotifier : IRegisteredObject
{
    public void Stop(bool immediate)
    {
        // do nothing, I only run tasks if I know that they won't
        // take more than a few seconds.
    }

    public void Started()
    {
        HostingEnvironment.RegisterObject(this);
    }

    public void Finished()
    {
        HostingEnvironment.UnregisterObject(this);
    }
}

进而

public class IISTaskFactory
{
    public static Task StartNew(Action act)
    {
        IISNotifier notif = new IISNotifier();
        notif.Started();
        return Task.Factory.StartNew(() => {
            act.Invoke();
            notif.Finished();
        });
    }
}

现在,当我想启动一个日志任务时,我只是做

return new LogResult(id, IISTaskFactory.StartNew(() => 
    DoLogInAzure(account, id, exception, request))
);

您可以在https://github.com/gmc-dev/IISTask查看(并下载代码)

于 2014-01-10T13:11:15.677 回答
0

信息还不够,但我怀疑它可能与 GC 和引用有关,如果它在您等待任务时有效。出于您的目的,更好的方法是使用 ETW (EventProvider) 并为每个请求设置 ActivityId。只需配置一个 ETW 会话就可以将所有消息重定向到一个文件。您可以向最终用户显示 ActivityId(一个 Guid)。

于 2012-10-01T02:22:31.537 回答
0

很抱歉没有将此添加为评论,我没有足够的代表。

https://msdn.microsoft.com/en-us/library/system.web.hosting.iregisteredobject(v=vs.110).aspx

应用程序只能有一个注册类型的实例。

这似乎表明 Gervasio Marchand 接受的答案有些不正确,因为每次调用他的静态辅助方法都会创建一个新的 IISNotifier,它是一个 IRegisteredObject。

于 2015-06-18T16:05:01.330 回答