如何一起使用任务和计时器

时间:2017-08-28 07:47:06

标签: .net vb.net multithreading time task

以下类用于将文本写入控制台,但是以更加时间的方式,比如像this这样的电影中显示字符的方式,问题是我无法获得方法在课堂上作为任务运行,特别是因为我使用计时器,我有多个相互关联的方法。

    Private NotInheritable Class WriteText
    Private Shared i As Integer = 0
    Private Shared text As String = ""
    Private Shared audio As New Audio
    Private Shared audioFile As String = "C:\Users\jamisco\Documents\Sound for Jamisco\beep-29.wav"
    Shared call_command As Boolean
    Shared timer As New Timer

    Public Shared Sub Text2Write(ByVal Text2Type As String, Optional ByVal call_awaitcommand As Boolean = False)
        text = Text2Type
        call_command = call_awaitcommand
        Timer.Interval = typingInterval
        Timer.AutoReset = True
        Timer.Start()
        writing2console = True
        FinishedTyping = False
        i = 0
        AddHandler Timer.Elapsed, AddressOf Tick
        audio.Play(audioFile, AudioPlayMode.BackgroundLoop)
        While (writing2console)
            Console.ReadKey(True)
        End While

    End Sub

    Private Shared Sub Tick(ByVal sender As Object, ByVal e As ElapsedEventArgs)

        If (text.Length >= 0 And timer.Enabled) Then
            If i < text.Length Then
                Console.Write(text(i))
                i += 1
            Else
                Reset()
            End If
        End If

    End Sub

    Public Shared Sub StopTyping()
        Reset()
    End Sub

    Private Shared Sub Reset()
        Console.WriteLine()
        audio.Stop()
        'timer.Stop()
        timer.Stop()
        i = 0
        writing2console = False
        FinishedTyping = True
        If (call_command) Then
            _jamisco.AwaitCommand()
        End If
    End Sub
End Class

1 个答案:

答案 0 :(得分:1)

这是你想要的效果吗?

enter image description here

using System;
using System.Threading.Tasks;

namespace asyncConsoleTyping
{
    class Program
    {
        private static Random _rnd = new Random();

        public static void Main(string[] args)
        {
            AsyncMain().Wait();
        }

        public static async Task AsyncMain()
        {
            await Type("This is a test");
            await Type("This is another test");
            await Type("What is your name?");
            var name = Console.ReadLine();
            await Type($"Hello {name}!");

            Console.Read();  
        }

        public static async Task Type(string text)
        {
            var prevColor = Console.ForegroundColor;
            Console.ForegroundColor=ConsoleColor.Green;
            foreach (char c in text)
            {
                Console.Write(c);
                await Task.Delay(10 + _rnd.Next(30));
            }
            Console.WriteLine();
            Console.ForegroundColor = prevColor;
        }
    }
}

这可以通过一次异步打印字符串来实现,每个字符之间有一个小的随机延迟。它还会在打印时更改文本颜色,然后再进行恢复。您可以使用await Type(SomeString)调用它,并且在打印字符时控制台不会锁定。一旦整个await被打印,执行将在string之后的下一行继续执行。它实际上并不使用Timer,因为有更合适的基于Task的机制(Task.Delay)。

您必须使用一个小“技巧”​​才能让控制台应用程序正确使用async await,因为Main不能是Task。只需创建一个AsyncMain函数,如我的示例所示,并使用MainAsyncMain().Wait()调用它。 C#7.1发布后,您不需要像public static Task Main(string[] args);(以及其他)will be considered a valid entry point那样执行此操作。

您可以阅读有关here的异步控制台应用程序的其他一些“问题”。

您可以使用async及其创建的Task取消对CancellationTokenSource CancellationToken的来电。

任何可以取消的Task都应该接受CancellationToken作为参数。该令牌是通过调用Token上的CancellationTokenSource函数创建的。创建源时,您可以指定超时值(var cts = new CancellationTokenSource(1000);),也可以从具有引用令牌的任何位置调用Cancelcts.Cancel)。然后,您的async方法可以将该令牌传递给它等待的任何内容(支持取消)。它还应定期检查IsCancellationRequested以确定是否应取消其工作。

这是上述示例的一个版本,支持取消:

using System;
using System.Threading;
using System.Threading.Tasks;

namespace asyncConsoleTyping
{
    class Program
    {
        private static Random _rnd = new Random();

        public static void Main(string[] args)
        {
            AsyncMain().Wait();
        }

        public static async Task AsyncMain()
        {
            await Type("This is a test");
            await Type("This is another test");
            await Type("What is your name?");
            var name = Console.ReadLine();
            await Type($"Hello {name}!");

            var cts = new CancellationTokenSource(1000); //Auto-cancels after 1 second
            try
            {
                await Type("This String can get canceled via a CancellationToken", cts.Token);
            }
            catch (Exception ex)
            {
                Console.ForegroundColor=ConsoleColor.Red;
                Console.WriteLine($"Canceled: {ex.Message}");
            }
        }

        public static Task Type(string text)
        {
            return Type(text, CancellationToken.None); //This overload doesn't support cancellation, but it calls the one that does.  Passing in CancellationToken.None allows it to work without checking to see if a "real" token was passed.
        }

        public static async Task Type(string text, CancellationToken ct)
        {
            var prevColor = Console.ForegroundColor;
            Console.ForegroundColor = ConsoleColor.Green;
            foreach (char c in text)
            {
                Console.Write(c);
                await Task.Delay(10 + _rnd.Next(30), ct); //Pass the Cancellationtoken in to Task.Delay so the delay can be canceled
                if (ct.IsCancellationRequested) //Check to see if the task was canceled, if so, exit the loop through the characters.
                {
                    break;
                }
            }
            Console.WriteLine();
            Console.ForegroundColor = prevColor;
        }
    }
}

上面的示例显示取消实际上有一个微妙的错误,可能导致控制台颜色不能重置。 await Task.Delay(10 + _rnd.Next(30), ct);会在取消时抛出异常,而不会进入颜色重置代码。这可以通过改变它来解决:

public static async Task Type(string text, CancellationToken ct)
{
    var prevColor = Console.ForegroundColor;
    Console.ForegroundColor = ConsoleColor.Green;
    try
    {
        foreach (char c in text)
        {
            Console.Write(c);
            await Task.Delay(10 + _rnd.Next(30), ct);
        }
    }
    finally
    {
        Console.WriteLine();
        Console.ForegroundColor = prevColor;
    }
    ct.ThrowIfCancellationRequested(); //Omit this line if you don't want an exception thrown on cancellation (but be aware of the consequences!)
}