在 javascript 中我们可以调用 SetTimeout 进行异步操作而不用担心线程安全问题,因为 javascript 是单线程的,SetTimeout 不会在新线程中执行代码块。
在 C# 中,我们可以使用 Task 类来使操作异步,如下所示。
Task.Factory.StartNew(()=> DoOperation());
但据我所知,DoOperation 可以在主线程或新线程中发生,而 Task 不允许我们决定是新线程还是在同一线程中。
新的异步功能不等同于 SetTimeout。
如何在 C# 应用程序中实现与 Javascript 完全相同的功能?有没有办法将控制台应用程序配置为单线程(我记得这样的事情)
编辑: 考虑以下我正在处理的用例场景。这是一个非常简化的版本。
class Program
{
static BufferBlock<int> queue = new BufferBlock<int>();
static List<int> list = new List<int>();
static void Main(string[] args)
{
Task.WhenAll(Produce(), Consume()).ContinueWith(r=> Console.WriteLine("list count: "+list.Count));
Console.ReadKey();
}
public static async Task Produce()
{
var random = new Random();
for (int i = 0; i < 1000; i++)
{
var value = random.Next();
await queue.SendAsync(value);
await Task.Delay(random.Next(1, 4));
list.Add(value);//manipulate none thread safe object
Console.WriteLine("value produced " + value);
}
queue.Complete();
}
public static async Task Consume()
{
var random = new Random();
while (await queue.OutputAvailableAsync())
{
var value = await queue.ReceiveAsync();
//consume the value
await Task.Delay(random.Next(1, 4));
list.Remove(value);//manipulate none thread safe object
Console.WriteLine("value consumed " + value);
}
}
}
基本上是生产者/消费者模式,生产者增加值(value),消费者消费这些值(value)。我们通过这个作业操作 List 对象,它不是线程安全的。
在任务列表的末尾,计数应该为 0,但这不是因为 2 任务正在访问非线程安全的 List 对象。我们当然可以像锁定一样处理线程安全问题,但在我的用例中这是不必要的,低效的,太多的锁定很难遵循并且容易陷入死锁问题。
@Asad 和其他人指出了正确的方向,所以感谢大家。
最佳答案
如果你只想确保不超过一个线程在运行(并且不一定所有的东西都在同一个线程上运行),你可以使用一个独占调度器来做到这一点:
static void Main(string[] args)
{
var sch = new ConcurrentExclusiveSchedulerPair().ExclusiveScheduler;
var tf = new TaskFactory(sch);
var t = tf.StartNew(() => Run()).Result;
t.Wait();
}
static async Task Run()
{
var start = DateTime.UtcNow;
var t = Task.Delay(1000).ContinueWith(_ =>
{
Console.WriteLine("I tried to log after 1 second, ended up logging after {0}", (DateTime.UtcNow - start).TotalSeconds);
});
Console.WriteLine("It has not yet been 1 second. I will hog the only thread available to demonstrate how I simulate JS behavior.");
Thread.Sleep(2000);
await t;
}
我正在使用 ConcurrentExclusiveSchedulerPair.ExclusiveScheduler
创建一个新的任务工厂,我将使用它来运行我的主程序入口点。
如果你运行这个程序,你会注意到即使日志记录被安排为 1 秒,它也会被阻塞,因为一个线程已经被占用而无所事事(这正是 JS 的行为方式)。移除 Sleep
并按时调用超时回调。
最后,这里是如何使用单线程异步模拟 JS setTimeout
行为:
static async Task JSSetTimeout(int ms, Action callback, int waitResolution = 10)
{
var startTime = DateTime.UtcNow;
while ((DateTime.UtcNow - startTime).TotalMilliseconds < ms)
{
await Task.Delay(waitResolution);
}
Console.WriteLine(Thread.CurrentThread.ManagedThreadId);
callback();
}
关于c# - 完全像 Javascript 的异步 C# 应用程序,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/30282350/