我开始练习Tasks,并尝试了以下代码:
static void Main()
{
Task.Factory.StartNew(() =>
{
Write('a', 0);
});
var t = new Task(() =>
{
Write('b', 10);
});
t.Start();
Write('c', 20);
Console.ReadLine();
}
static void Write(char c, int x)
{
int yCounter = 0;
for (int i = 0; i < 1000; i++)
{
Console.WriteLine(c);
Console.SetCursorPosition(x, yCounter);
yCounter++;
Thread.Sleep(100);
}
}
我的想法是看控制台如何在三个不同的列之间移动以输出不同的字符。它会交换列,但不会输出正确的字符。例如,在第一列中,它仅需要输出“ a”,但也需要输出“ b”和“ c”,其他两列也是如此。
答案 0 :(得分:4)
这可能是一个使用任务的特别糟糕的示例-或一个如何严重使用任务的示例。
在您的任务中,您要设置一个全局状态(SetCursorPosition
),这当然会影响其他任务(Console
毕竟是静态的)。
Console.WriteLine('b')
在将游标设置为0
,10
或20
之后调用,反之亦然。任务不应依赖于可能已更改的任何全局(或类级别)状态(除非任务可以更改值)。关于示例,您必须确保在编写输出之前,没有其他任务调用SetCursorPosition
。实现此目的的最简单方法是锁定任务
private static object lockObject = new object(); // you need an object of a reference type for locking
static void Write(char c, int x)
{
int yCounter = 0;
for (int i = 0; i < 1000; i++)
{
lock(lockObject)
{
Console.SetCursorPosition(x, yCounter);
Console.Write(c);
}
yCounter++;
Thread.Sleep(100);
}
}
lock
确保一次没有两个任务进入该块(假设锁定对象与相同相同),因此每个任务都可以将光标设置到其位置想要在没有任何其他将光标设置到任何其他位置的任务的情况下进行写入和写入其字符。 (此外,我已经交换了Write
和SetCursorPosition
,因为我们必须在写入输出之前调用SetCursorPosition
-无论如何,如果不交换这两条线,该锁将无用。 )
答案 1 :(得分:2)
除了保罗的回答。
如果您要处理任务和async
/ await
,请不要以任何方式混合使用Task
和Thread
。
使用Write
/ Task.Run
执行Task.Start
方法称为“异步同步”。这是一种不良做法,应该避免。
这是您的代码,以异步方式重写,并具有异步同步:
class Program
{
static void Main(string[] args)
{
var asyncLock = new AsyncLock();
// we need ToList here, since IEnumerable is lazy,
// and must be enumerated to produce values (tasks in this case);
// WriteAsync call inside Select produces a "hot" task - task, that is already scheduled;
// there's no need to start hot tasks explicitly - they are already started
new[] { ('a', 0), ('b', 10), ('c', 20) }
.Select(_ => WriteAsync(_.Item1, _.Item2, asyncLock))
.ToList();
Console.ReadLine();
}
static async Task WriteAsync(char c, int x, AsyncLock asyncLock)
{
for (var i = 0; i < 1000; i++)
{
using (await asyncLock.LockAsync())
{
Console.SetCursorPosition(x, i);
Console.Write(c);
}
await Task.Delay(100);
}
}
}
AsyncLock
位于Nito.AsyncEx包中。