Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/265.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# 将长时间运行的任务与异步/等待模式结合起来的正确方法是什么?_C#_Xamarin.ios_Task Parallel Library_Async Await_Long Running Processes - Fatal编程技术网

C# 将长时间运行的任务与异步/等待模式结合起来的正确方法是什么?

C# 将长时间运行的任务与异步/等待模式结合起来的正确方法是什么?,c#,xamarin.ios,task-parallel-library,async-await,long-running-processes,C#,Xamarin.ios,Task Parallel Library,Async Await,Long Running Processes,我有一个“高精度”计时器类,我需要能够启动、停止和暂停/恢复。为了做到这一点,我结合了我在互联网上找到的几个不同的例子,但我不确定我是否正确使用asnyc/await的任务 这是我的相关代码: //based on http://haukcode.wordpress.com/2013/01/29/high-precision-timer-in-netc/ public class HighPrecisionTimer : IDisposable { Task _task; Can

我有一个“高精度”计时器类,我需要能够启动、停止和暂停/恢复。为了做到这一点,我结合了我在互联网上找到的几个不同的例子,但我不确定我是否正确使用asnyc/await的任务

这是我的相关代码:

//based on http://haukcode.wordpress.com/2013/01/29/high-precision-timer-in-netc/
public class HighPrecisionTimer : IDisposable
{
    Task _task;
    CancellationTokenSource _cancelSource;

    //based on http://blogs.msdn.com/b/pfxteam/archive/2013/01/13/cooperatively-pausing-async-methods.aspx
    PauseTokenSource _pauseSource;

    Stopwatch _watch;
    Stopwatch Watch { get { return _watch ?? (_watch = Stopwatch.StartNew()); } }

    public bool IsPaused
    {
        get { return _pauseSource != null && _pauseSource.IsPaused; }
        private set
        {
            if (value)
            {
                _pauseSource = new PauseTokenSource();
            }
            else
            {
                _pauseSource.IsPaused = false;
            }
        }
    }

    public bool IsRunning { get { return !IsPaused && _task != null && _task.Status == TaskStatus.Running; } }

    public void Start()
    {
        if (IsPaused)
        {
            IsPaused = false;
        }
        else if (!IsRunning)
        {
            _cancelSource = new CancellationTokenSource();
            _task = new Task(ExecuteAsync, _cancelSource.Token, TaskCreationOptions.LongRunning);
            _task.Start();
        }
    }

    public void Stop()
    {
        if (_cancelSource != null)
        {
            _cancelSource.Cancel();
        }
    }

    public void Pause()
    {
        if (!IsPaused)
        {
            if (_watch != null)
            {
                _watch.Stop();
            }
        }

        IsPaused = !IsPaused;
    }

    async void ExecuteAsync()
    {
        while (!_cancelSource.IsCancellationRequested)
        {
            if (_pauseSource != null && _pauseSource.IsPaused)
            {
                await _pauseSource.Token.WaitWhilePausedAsync();
            }

            // DO CUSTOM TIMER STUFF...
        }

        if (_watch != null)
        {
            _watch.Stop();
            _watch = null;
        }

        _cancelSource = null;
        _pauseSource = null;
    }

    public void Dispose()
    {
        if (IsRunning)
        {
            _cancelSource.Cancel();
        }
    }
}
任何人都可以看一下,并为我提供一些关于我是否这样做是正确的指针吗

更新

我已经尝试过修改下面的代码,但是我仍然无法理解语法。每次尝试将ExecuteAsync()方法传递给TaskFactory.StartNewTask.Run,都会导致如下编译错误:

“以下方法或属性之间的调用不明确:TaskFactory.StartNew(Action,CancellationToken…)和TaskFactory.StartNew(Func,CancellationToken…)

最后,有没有一种方法可以在不提供TaskScheduler的情况下指定LongRunning TaskCreationOption

async **Task** ExecuteAsync()
{
    while (!_cancelSource.IsCancellationRequested)
    {
        if (_pauseSource != null && _pauseSource.IsPaused)
        {
            await _pauseSource.Token.WaitWhilePausedAsync();
        }
        //...
    }
}

public void Start()
{
    //_task = Task.Factory.StartNew(ExecuteAsync, _cancelSource.Token, TaskCreationOptions.LongRunning, null);

    //_task = Task.Factory.StartNew(ExecuteAsync, _cancelSource.Token);

    //_task = Task.Run(ExecuteAsync, _cancelSource.Token);

}
更新2

我想我已经缩小了范围,但仍然不能确定正确的语法。这是创建任务的正确方法吗?这样,使用者/调用代码就可以继续运行,任务将在一个新的异步线程上旋转并启动

_task = Task.Run(async () => await ExecuteAsync, _cancelSource.Token);

//**OR**

_task = Task.Factory.StartNew(async () => await ExecuteAsync, _cancelSource.Token, TaskCreationOptions.LongRunning, TaskScheduler.Default);
以下是几点:

  • async void
    方法仅适用于异步事件处理程序()。您的
    async void ExecuteAsync()
    会立即返回(只要代码流到达
    wait\u pauseSource
    )。基本上,您的
    \u任务
    在此之后处于已完成状态,而其余的
    ExecuteAsync
    将不被观察地执行(因为它是
    void
    )。它甚至可能根本不会继续执行,这取决于主线程(以及进程)何时终止

  • 考虑到这一点,您应该将其设置为
    async Task ExecuteAsync()
    ,并使用
    Task.Run
    Task.Factory.StartNew
    而不是
    new Task
    来启动它。因为您希望任务的操作方法是
    async
    ,所以您需要在此处处理嵌套的任务,即
    task
    ,它
    task.Run
    将自动为您展开。更多信息可以找到和

  • 采用以下方法(根据设计,AFAIU):代码的使用者端(调用
    Pause
    )实际上只请求暂停,但不同步。它将在
    Pause
    之后继续执行,即使生产者方可能尚未达到等待状态,即
    wait\u pauseSource.Token.WaitWhilePausedAsync()
    。这对于你的应用程序逻辑来说可能没问题,但你应该意识到这一点。更多信息

[UPDATE]下面是使用
工厂的正确语法。StartNew
。注意
任务
任务。展开
。另请注意
\u task.Wait()
停止
中,它用于确保任务在
停止
返回时完成(类似于
线程.Join
)。另外,
TaskScheduler.Default
用于指示
Factory.StartNew
使用线程池调度程序。如果您从另一个任务中创建
HighPrecisionTimer
对象,这一点很重要,而该任务又是在具有非默认同步上下文的线程上创建的,例如UI线程(更多信息和)

使用系统;
使用系统线程;
使用System.Threading.Tasks;
命名空间控制台应用程序
{
公共类高精度定时器
{
任务-任务;
CancellationTokenSource\u cancelSource;
公开作废开始()
{
_cancelSource=新的CancellationTokenSource();
Task Task=Task.Factory.StartNew(
功能:ExecuteAsync,
cancellationToken:\u cancelSource.Token,
creationOptions:TaskCreationOptions.LongRunning,
调度程序:TaskScheduler.Default);
_task=task.Unwrap();
}
公共停车场()
{
_cancelSource.Cancel();//请求取消
_task.Wait();//等待任务完成
}
异步任务ExecuteAsync()
{
Console.WriteLine(“输入ExecuteAsync”);
而(!\u cancelSource.IsCancellationRequested)
{
等待任务。延迟(42);//用于测试
//做自定义定时器的东西。。。
}
Console.WriteLine(“Exit ExecuteAsync”);
}
}
班级计划
{
公共静态void Main()
{
var highPrecisionTimer=新的highPrecisionTimer();
控制台写入线(“启动计时器”);
高精度定时器。启动();
《睡眠》(2000年);
控制台写入线(“停止计时器”);
高精度定时器。停止();
Console.WriteLine(“按Enter键退出…”);
Console.ReadLine();
}
}
}

@JoshuaBarker,我已经用一些代码更新了我的答案,这些代码显示了
Task.Factory.StartNew
的正确语法,还有一些想法。一切都应该适用于Mono运行时环境。我相信它们非常符合.NET规范。因此,我最终创建了两个变量,一个用于任务,另一个仅用于任务。有趣的注意,但当我在调试窗口中检查这两个变量时,它的状态是“正在运行”的“外部”任务包装器,而内部任务总是“等待激活”。。。因此,调用pause现在可以工作了,因为我可以暂停/恢复我正在运行的线程,但iOS或MonoTouch中的一些奇怪的东西会导致该线程从后台线程(#5)移动到主UI线程(#1)。。。向前两步,向后一步…好的。。。谢谢你的澄清。看起来这可能是Xamarin实现T的一个bug
using System;
using System.Threading;
using System.Threading.Tasks;

namespace ConsoleApplication
{
    public class HighPrecisionTimer
    {
        Task _task;
        CancellationTokenSource _cancelSource;

        public void Start()
        {
            _cancelSource = new CancellationTokenSource();

            Task<Task> task = Task.Factory.StartNew(
                function: ExecuteAsync, 
                cancellationToken: _cancelSource.Token, 
                creationOptions: TaskCreationOptions.LongRunning, 
                scheduler: TaskScheduler.Default);

            _task = task.Unwrap();
        }

        public void Stop()
        {
            _cancelSource.Cancel(); // request the cancellation

            _task.Wait(); // wait for the task to complete
        }

        async Task ExecuteAsync()
        {
            Console.WriteLine("Enter ExecuteAsync");
            while (!_cancelSource.IsCancellationRequested)
            {
                await Task.Delay(42); // for testing

                // DO CUSTOM TIMER STUFF...
            }
            Console.WriteLine("Exit ExecuteAsync");
        }
    }

    class Program
    {
        public static void Main()
        {
            var highPrecisionTimer = new HighPrecisionTimer();

            Console.WriteLine("Start timer");
            highPrecisionTimer.Start();

            Thread.Sleep(2000);

            Console.WriteLine("Stop timer");
            highPrecisionTimer.Stop();

            Console.WriteLine("Press Enter to exit...");
            Console.ReadLine();
        }
    }
}