Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/298.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 使用Task.Run而不是Delegate.BeginInvoke_C#_Asynchronous - Fatal编程技术网

C# 使用Task.Run而不是Delegate.BeginInvoke

C# 使用Task.Run而不是Delegate.BeginInvoke,c#,asynchronous,C#,Asynchronous,我最近将我的项目升级到ASP.NET 4.5,我已经等了很长时间才使用4.5的异步功能。阅读文档后,我不确定是否可以改进代码 我希望异步执行任务,然后忘记它。我目前的做法是创建代理,然后使用BeginInvoke 下面是我的项目中的一个过滤器,每当用户访问必须审核的资源时,它都会在我们的数据库中创建一个审核: public override void OnActionExecuting(ActionExecutingContext filterContext) { var request

我最近将我的项目升级到ASP.NET 4.5,我已经等了很长时间才使用4.5的异步功能。阅读文档后,我不确定是否可以改进代码

我希望异步执行任务,然后忘记它。我目前的做法是创建代理,然后使用
BeginInvoke

下面是我的项目中的一个过滤器,每当用户访问必须审核的资源时,它都会在我们的数据库中创建一个审核:

public override void OnActionExecuting(ActionExecutingContext filterContext)
{
    var request = filterContext.HttpContext.Request;
    var id = WebSecurity.CurrentUserId;

    var invoker = new MethodInvoker(delegate
    {
        var audit = new Audit
        {
            Id = Guid.NewGuid(),
            IPAddress = request.UserHostAddress,
            UserId = id,
            Resource = request.RawUrl,
            Timestamp = DateTime.UtcNow
        };

        var database = (new NinjectBinder()).Kernel.Get<IDatabaseWorker>();
        database.Audits.InsertOrUpdate(audit);
        database.Save();
    });

    invoker.BeginInvoke(StopAsynchronousMethod, invoker);

    base.OnActionExecuting(filterContext);
}
我宁愿不使用回调,因为我不需要异步调用的任务的结果


如何使用
Task.Run()
(或
async
wait
)改进此代码?

如果我正确理解了您的需求,您希望启动一项任务,然后忘记它。当任务完成时,如果发生异常,则要将其记录下来

我会使用
Task.Run
创建一个任务,然后使用
ContinueWith
附加一个继续任务。此继续任务将记录从父任务引发的任何异常。另外,使用
TaskContinuationOptions.OnlyOnFaulted
确保仅在发生异常时才运行继续

Task.Run(() => {
    var audit = new Audit
        {
            Id = Guid.NewGuid(),
            IPAddress = request.UserHostAddress,
            UserId = id,
            Resource = request.RawUrl,
            Timestamp = DateTime.UtcNow
        };

    var database = (new NinjectBinder()).Kernel.Get<IDatabaseWorker>();
    database.Audits.InsertOrUpdate(audit);
    database.Save();

}).ContinueWith(task => {
    task.Exception.Handle(ex => {
        var username = WebSecurity.CurrentUserName;
        Debugging.DispatchExceptionEmail(ex, username);
    });

}, TaskContinuationOptions.OnlyOnFaulted);
Task.Run(()=>{
var审计=新审计
{
Id=Guid.NewGuid(),
IPAddress=request.UserHostAddress,
UserId=id,
Resource=request.RawUrl,
Timestamp=DateTime.UtcNow
};
var数据库=(新的NinjectBinder()).Kernel.Get();
数据库.Audits.InsertOrUpdate(审计);
Save();
}).ContinueWith(任务=>{
task.Exception.Handle(ex=>{
var username=WebSecurity.CurrentUserName;
调试.DispatchExceptionEmail(例如,用户名);
});
},TaskContinuationOptions.OnlyOnFaulted);
作为补充说明,ASP.NET中的后台任务和触发并忘记场景是非常不鼓励的。看

下面是我的项目中的一个过滤器,每当用户访问必须审核的资源时,它都会在我们的数据库中创建一个审核

审计当然不是我所说的“一劳永逸”。请记住,在ASP.NET上。因此,如果您想要的语义是审计可能偶尔会丢失,那么(并且只有在那时)您可以使用fire和forget进行审计

如果要确保您的审核都是正确的,请等待审核保存完成,然后再发送响应,或者将审核信息排队到可靠的存储(例如Azure队列或MSMQ),并让独立的后端(例如Azure worker角色或Win32服务)处理该队列中的审核

但是,如果您想过一种危险的生活(承认偶尔会丢失审计),您可以通过向ASP.NET运行时注册工作来缓解问题。使用:

public override void OnActionExecuting(ActionExecutingContext filterContext)
{
var request=filterContext.HttpContext.request;
var id=WebSecurity.CurrentUserId;
BackgroundTaskManager.Run(()=>
{
尝试
{
var审计=新审计
{
Id=Guid.NewGuid(),
IPAddress=request.UserHostAddress,
UserId=id,
Resource=request.RawUrl,
Timestamp=DateTime.UtcNow
};
var数据库=(新的NinjectBinder()).Kernel.Get();
数据库.Audits.InsertOrUpdate(审计);
Save();
}
捕获(例外e)
{
var username=WebSecurity.CurrentUserName;
调试。DispatchExceptionEmail(电子邮件,用户名);
}
});
base.OnActionExecuting(filterContext);
}

这听起来可能有点超出范围,但如果您只是想在启动它之后忘记它,为什么不直接使用
线程池呢

比如:

ThreadPool.QueueUserWorkItem(
            x =>
                {
                    try
                    {
                        // Do something
                        ...
                    }
                    catch (Exception e)
                    {
                        // Log something
                        ...
                    }
                });
我不得不对不同的异步调用方法进行一些性能基准测试,我发现(毫不奇怪)
ThreadPool
工作得更好,但实际上,
BeginInvoke
并没有那么糟糕(我在.NET 4.5上)。这就是我在文章末尾发现的代码。我没有在网上找到类似的东西,所以我自己花时间检查了一下。每个调用并不完全相等,但就其功能而言,在功能上或多或少是相等的:

  • 线程池
    :70.80ms
  • 任务
    :90.88毫秒
  • BeginInvoke
    :121.88ms
  • 螺纹
    :4657.52ms

    public class Program
    {
        public delegate void ThisDoesSomething();
    
        // Perform a very simple operation to see the overhead of
        // different async calls types.
        public static void Main(string[] args)
        {
            const int repetitions = 25;
            const int calls = 1000;
            var results = new List<Tuple<string, double>>();
    
            Console.WriteLine(
                "{0} parallel calls, {1} repetitions for better statistics\n", 
                calls, 
                repetitions);
    
            // Threads
            Console.Write("Running Threads");
            results.Add(new Tuple<string, double>("Threads", RunOnThreads(repetitions, calls)));
            Console.WriteLine();
    
            // BeginInvoke
            Console.Write("Running BeginInvoke");
            results.Add(new Tuple<string, double>("BeginInvoke", RunOnBeginInvoke(repetitions, calls)));
            Console.WriteLine();
    
            // Tasks
            Console.Write("Running Tasks");
            results.Add(new Tuple<string, double>("Tasks", RunOnTasks(repetitions, calls)));
            Console.WriteLine();
    
            // Thread Pool
            Console.Write("Running Thread pool");
            results.Add(new Tuple<string, double>("ThreadPool", RunOnThreadPool(repetitions, calls)));
            Console.WriteLine();
            Console.WriteLine();
    
            // Show results
            results = results.OrderBy(rs => rs.Item2).ToList();
            foreach (var result in results)
            {
                Console.WriteLine(
                    "{0}: Done in {1}ms avg", 
                    result.Item1,
                    (result.Item2 / repetitions).ToString("0.00"));
            }
    
            Console.WriteLine("Press a key to exit");
            Console.ReadKey();
        }
    
        /// <summary>
        /// The do stuff.
        /// </summary>
        public static void DoStuff()
        {
            Console.Write("*");
        }
    
        public static double RunOnThreads(int repetitions, int calls)
        {
            var totalMs = 0.0;
            for (var j = 0; j < repetitions; j++)
            {
                Console.Write(".");
                var toProcess = calls;
                var stopwatch = new Stopwatch();
                var resetEvent = new ManualResetEvent(false);
                var threadList = new List<Thread>();
                for (var i = 0; i < calls; i++)
                {
                    threadList.Add(new Thread(() =>
                    {
                        // Do something
                        DoStuff();
    
                        // Safely decrement the counter
                        if (Interlocked.Decrement(ref toProcess) == 0)
                        {
                            resetEvent.Set();
                        }
                    }));
                }
    
                stopwatch.Start();
                foreach (var thread in threadList)
                {
                    thread.Start();
                }
    
                resetEvent.WaitOne();
                stopwatch.Stop();
                totalMs += stopwatch.ElapsedMilliseconds;
            }
    
            return totalMs;
        }
    
        public static double RunOnThreadPool(int repetitions, int calls)
        {
            var totalMs = 0.0;
            for (var j = 0; j < repetitions; j++)
            {
                Console.Write(".");
                var toProcess = calls;
                var resetEvent = new ManualResetEvent(false);
                var stopwatch = new Stopwatch();
                var list = new List<int>();
                for (var i = 0; i < calls; i++)
                {
                    list.Add(i);
                }
    
                stopwatch.Start();
                for (var i = 0; i < calls; i++)
                {
                    ThreadPool.QueueUserWorkItem(
                        x =>
                        {
                            // Do something
                            DoStuff();
    
                            // Safely decrement the counter
                            if (Interlocked.Decrement(ref toProcess) == 0)
                            {
                                resetEvent.Set();
                            }
                        },
                        list[i]);
                }
    
                resetEvent.WaitOne();
                stopwatch.Stop();
                totalMs += stopwatch.ElapsedMilliseconds;
            }
    
            return totalMs;
        }
    
        public static double RunOnBeginInvoke(int repetitions, int calls)
        {
            var totalMs = 0.0;
            for (var j = 0; j < repetitions; j++)
            {
                Console.Write(".");
                var beginInvokeStopwatch = new Stopwatch();
                var delegateList = new List<ThisDoesSomething>();
                var resultsList = new List<IAsyncResult>();
                for (var i = 0; i < calls; i++)
                {
                    delegateList.Add(DoStuff);
                }
    
                beginInvokeStopwatch.Start();
                foreach (var delegateToCall in delegateList)
                {
                    resultsList.Add(delegateToCall.BeginInvoke(null, null));
                }
    
                // We lose a bit of accuracy, but if the loop is big enough,
                // it should not really matter
                while (resultsList.Any(rs => !rs.IsCompleted))
                {
                    Thread.Sleep(10);
                }
    
                beginInvokeStopwatch.Stop();
                totalMs += beginInvokeStopwatch.ElapsedMilliseconds;
            }
    
            return totalMs;
        }
    
        public static double RunOnTasks(int repetitions, int calls)
        {
            var totalMs = 0.0;
            for (var j = 0; j < repetitions; j++)
            {
                Console.Write(".");
                var resultsList = new List<Task>();
                var stopwatch = new Stopwatch();
                stopwatch.Start();
                for (var i = 0; i < calls; i++)
                {
                    resultsList.Add(Task.Factory.StartNew(DoStuff));
                }
    
                // We lose a bit of accuracy, but if the loop is big enough,
                // it should not really matter
                while (resultsList.Any(task => !task.IsCompleted))
                {
                    Thread.Sleep(10);
                }
    
                stopwatch.Stop();
                totalMs += stopwatch.ElapsedMilliseconds;
            }
    
            return totalMs;
        }
    }
    
    公共类程序
    {
    公共委托无效ThisDoesMething();
    //执行一个非常简单的操作以查看
    //不同的异步调用类型。
    公共静态void Main(字符串[]args)
    {
    常量整数重复=25;
    const int calls=1000;
    var results=新列表();
    控制台写入线(
    “{0}个并行调用,{1}次重复以获得更好的统计信息\n”,
    电话,
    重复);
    //线程
    编写(“运行线程”);
    结果:添加(新元组(“线程”,RunOnThreads(重复,调用));
    Console.WriteLine();
    //开始觉醒
    Console.Write(“Running BeginInvoke”);
    结果.添加(新元组(“BeginInvoke”,RunOnBeginInvoke(重复,调用));
    Console.WriteLine();
    //任务
    Console.Write(“正在运行的任务”);
    结果.添加(新元组(“任务”,runontask(重复,调用));
    Console.WriteLine();
    //线程池
    Write(“运行线程池”);
    结果:添加(新元组(“ThreadPool”、runnthreadpool(重复、调用));
    Console.WriteLine();
    Console.WriteLine();
    //显示结果
    results=results.OrderBy(rs=>rs.Item2.ToList();
    foreach(结果中的var结果)
    {
    控制台写入线(
    “{0}:在{1}ms avg中完成”,
    结果.第1项,
    (res
    
    ThreadPool.QueueUserWorkItem(
                x =>
                    {
                        try
                        {
                            // Do something
                            ...
                        }
                        catch (Exception e)
                        {
                            // Log something
                            ...
                        }
                    });
    
    public class Program
    {
        public delegate void ThisDoesSomething();
    
        // Perform a very simple operation to see the overhead of
        // different async calls types.
        public static void Main(string[] args)
        {
            const int repetitions = 25;
            const int calls = 1000;
            var results = new List<Tuple<string, double>>();
    
            Console.WriteLine(
                "{0} parallel calls, {1} repetitions for better statistics\n", 
                calls, 
                repetitions);
    
            // Threads
            Console.Write("Running Threads");
            results.Add(new Tuple<string, double>("Threads", RunOnThreads(repetitions, calls)));
            Console.WriteLine();
    
            // BeginInvoke
            Console.Write("Running BeginInvoke");
            results.Add(new Tuple<string, double>("BeginInvoke", RunOnBeginInvoke(repetitions, calls)));
            Console.WriteLine();
    
            // Tasks
            Console.Write("Running Tasks");
            results.Add(new Tuple<string, double>("Tasks", RunOnTasks(repetitions, calls)));
            Console.WriteLine();
    
            // Thread Pool
            Console.Write("Running Thread pool");
            results.Add(new Tuple<string, double>("ThreadPool", RunOnThreadPool(repetitions, calls)));
            Console.WriteLine();
            Console.WriteLine();
    
            // Show results
            results = results.OrderBy(rs => rs.Item2).ToList();
            foreach (var result in results)
            {
                Console.WriteLine(
                    "{0}: Done in {1}ms avg", 
                    result.Item1,
                    (result.Item2 / repetitions).ToString("0.00"));
            }
    
            Console.WriteLine("Press a key to exit");
            Console.ReadKey();
        }
    
        /// <summary>
        /// The do stuff.
        /// </summary>
        public static void DoStuff()
        {
            Console.Write("*");
        }
    
        public static double RunOnThreads(int repetitions, int calls)
        {
            var totalMs = 0.0;
            for (var j = 0; j < repetitions; j++)
            {
                Console.Write(".");
                var toProcess = calls;
                var stopwatch = new Stopwatch();
                var resetEvent = new ManualResetEvent(false);
                var threadList = new List<Thread>();
                for (var i = 0; i < calls; i++)
                {
                    threadList.Add(new Thread(() =>
                    {
                        // Do something
                        DoStuff();
    
                        // Safely decrement the counter
                        if (Interlocked.Decrement(ref toProcess) == 0)
                        {
                            resetEvent.Set();
                        }
                    }));
                }
    
                stopwatch.Start();
                foreach (var thread in threadList)
                {
                    thread.Start();
                }
    
                resetEvent.WaitOne();
                stopwatch.Stop();
                totalMs += stopwatch.ElapsedMilliseconds;
            }
    
            return totalMs;
        }
    
        public static double RunOnThreadPool(int repetitions, int calls)
        {
            var totalMs = 0.0;
            for (var j = 0; j < repetitions; j++)
            {
                Console.Write(".");
                var toProcess = calls;
                var resetEvent = new ManualResetEvent(false);
                var stopwatch = new Stopwatch();
                var list = new List<int>();
                for (var i = 0; i < calls; i++)
                {
                    list.Add(i);
                }
    
                stopwatch.Start();
                for (var i = 0; i < calls; i++)
                {
                    ThreadPool.QueueUserWorkItem(
                        x =>
                        {
                            // Do something
                            DoStuff();
    
                            // Safely decrement the counter
                            if (Interlocked.Decrement(ref toProcess) == 0)
                            {
                                resetEvent.Set();
                            }
                        },
                        list[i]);
                }
    
                resetEvent.WaitOne();
                stopwatch.Stop();
                totalMs += stopwatch.ElapsedMilliseconds;
            }
    
            return totalMs;
        }
    
        public static double RunOnBeginInvoke(int repetitions, int calls)
        {
            var totalMs = 0.0;
            for (var j = 0; j < repetitions; j++)
            {
                Console.Write(".");
                var beginInvokeStopwatch = new Stopwatch();
                var delegateList = new List<ThisDoesSomething>();
                var resultsList = new List<IAsyncResult>();
                for (var i = 0; i < calls; i++)
                {
                    delegateList.Add(DoStuff);
                }
    
                beginInvokeStopwatch.Start();
                foreach (var delegateToCall in delegateList)
                {
                    resultsList.Add(delegateToCall.BeginInvoke(null, null));
                }
    
                // We lose a bit of accuracy, but if the loop is big enough,
                // it should not really matter
                while (resultsList.Any(rs => !rs.IsCompleted))
                {
                    Thread.Sleep(10);
                }
    
                beginInvokeStopwatch.Stop();
                totalMs += beginInvokeStopwatch.ElapsedMilliseconds;
            }
    
            return totalMs;
        }
    
        public static double RunOnTasks(int repetitions, int calls)
        {
            var totalMs = 0.0;
            for (var j = 0; j < repetitions; j++)
            {
                Console.Write(".");
                var resultsList = new List<Task>();
                var stopwatch = new Stopwatch();
                stopwatch.Start();
                for (var i = 0; i < calls; i++)
                {
                    resultsList.Add(Task.Factory.StartNew(DoStuff));
                }
    
                // We lose a bit of accuracy, but if the loop is big enough,
                // it should not really matter
                while (resultsList.Any(task => !task.IsCompleted))
                {
                    Thread.Sleep(10);
                }
    
                stopwatch.Stop();
                totalMs += stopwatch.ElapsedMilliseconds;
            }
    
            return totalMs;
        }
    }