c# - 完全像 Javascript 的异步 C# 应用程序

标签 c# multithreading asynchronous

在 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/

相关文章:

python - 为什么 async pysnmp 这么慢?

java - 程序名称不必与类名称相同 - C#

c# - 比较数字得到不同的结果

java - 我是编程的新手,需要一些帮助和建议

multithreading - Perl CGI 线程

c# - C#中的多个并发定期刷新操作

node.js - 同步 Mongoose 请求

java - 执行依赖于其他任务的 CompletableFuture

c# - 在 IdentityServer3 中处理 CORS 之前发送的飞行前 OPTIONS 请求

C# 静态成员问题