当任务Y延迟或休眠时,如何运行任务X?

huangapple go评论87阅读模式
英文:

How to run task X when task Y is delayed or sleeping?

问题

I will provide a translation of the code and the summary of your requirements.

以下是您的代码的翻译:

当我安排了很多任务,而有些任务进入延迟模式时,我希望其他任务开始运行,但以下代码表明这种情况并没有发生。

我将“MaxDegreeOfParallelism”限制为3,您可以看到只有在前3个任务完成后才激活了一个新的3个任务组,尽管在前三个任务进入延迟状态时立即开始它们是可能的。

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

namespace ConsoleApp1
{
    class Program
    {
        static void Main(string[] args)
        {
            var s = new Stopwatch();
            s.Start();
            Parallel.For(1, 12, new ParallelOptions() { MaxDegreeOfParallelism = 3 }, (i) =>
            {
                Debug.WriteLine($"Task: {i}, Elapsed: {s.Elapsed}");
                Task.WaitAll(Task.Delay(5000));
            });
        }
    }
}

要满足您的要求,您可能需要考虑使用线程或异步任务。这是您的要求的总结:

总结我需要的:

  1. 同时执行多个操作,按照顺序。
  2. 有可能将操作限制为同时执行X个操作。
  3. 如果一个操作进入待机或休眠模式,另一个可以开始或继续工作。
  4. 当一个操作完成休眠期后,它以未曾启动的新操作的方式按照顺序返回工作。
英文:

When I schedule a lot of tasks, and some tasks go into delay mode, I would expect others to start running, but the following code demonstrates that this does not happen.

I limited the MaxDegreeOfParallelism to 3, you can see that a new group of 3 tasks is activated only after the first 3 are finished, although it was possible to start them immediately when the first three entered the delay.

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

namespace ConsoleApp1
{
    class Program
    {
        static void Main(string[] args)
        {
            var s = new Stopwatch();
            s.Start();
            Parallel.For(1, 12, new ParallelOptions() { MaxDegreeOfParallelism = 3 }, (i) =>
            {
                Debug.WriteLine($"Task: {i}, Elapsed: {s.Elapsed}");
                Task.WaitAll(Task.Delay(5000));
            });
        }
    }
}

Output:

Task: 4, Elapsed: 00:00:00.0811475
Task: 1, Elapsed: 00:00:00.0811470
Task: 7, Elapsed: 00:00:00.0811479
Task: 2, Elapsed: 00:00:05.0972314
Task: 8, Elapsed: 00:00:05.1036003
Task: 5, Elapsed: 00:00:05.1036003
Task: 9, Elapsed: 00:00:10.1058859
Task: 3, Elapsed: 00:00:10.1101862
Task: 6, Elapsed: 00:00:10.1356772
Task: 10, Elapsed: 00:00:15.1183184
Task: 11, Elapsed: 00:00:20.1289868

The same output is obtained in both TargetFramework net48 and TargetFramework netcoreapp3.1.

How is it possible to make that when some of the tasks are in sleep or delay mode then others will come into action?

As far as I know, Windows behaves with threads, that when one sleeps, Windows switches to run another thread. Similarly, I also need tasks.

Maybe I should work with something else and not tasks? Maybe with threads? But how exactly?

To summarize what I need:

  1. Run many actions at the same time and according to the turn.
  2. Possibility to limit the operations to X operations at the same time.
  3. In the event that one operation enters standby or sleep mode, another can start or continue work.
  4. When an action finishes the sleep period it returns to work according to the turn as a new action that never started.

答案1

得分: 2

> I limited the MaxDegreeOfParallelism to 3, you can see that a new group of 3 tasks is activated only after the first 3 are finished, although it was possible to start them immediately when the first three entered the delay.

我将MaxDegreeOfParallelism限制为3,你可以看到只有在前3个任务完成后才会激活新的3个任务组,尽管在前三个进入延迟时也有可能立即启动它们。

No, it is not possible from the Parallel.For standpoint of view. MaxDegreeOfParallelism means that it will have at most 3 actions executing in parallel no matter what they are doing.

不,从Parallel.For的角度来看是不可能的。MaxDegreeOfParallelism意味着无论正在执行什么操作,最多只能同时执行3个动作。

Also note Parallel.For is not Task-aware (so no fancy async-await stuff handling) and Task.WaitAll is effectively a blocking call which for example does not allow returning of the executing thread into the thread pool.

还要注意的是,Parallel.For不支持任务(Task-aware),因此不能处理复杂的异步等待操作,而Task.WaitAll实际上是一个阻塞调用,例如不允许将执行线程返回到线程池。

> As far as I know, Windows behaves with threads, that when one sleeps, Windows switches to run another thread. Similarly, I also need tasks.

就我所知,Windows在处理线程时,当一个线程休眠时,Windows会切换到运行另一个线程。同样,我也需要任务

That is handled by the ThreadPool. Essentially this was one the goals of async-await introduction - to easily handle IO-bound operations allowing to reuse the threads to do meaningful work while doing the IO waiting. You can just start all of your tasks and let the thread pool handle the rest (though there are some potential issues - you can't create a custom thread pool in .NET and you can end up with thread pool starvation).

这由ThreadPool处理。基本上,这是引入async-await的一个目标之一,它可以轻松处理IO绑定操作,允许重用线程来执行有意义的工作,同时等待IO。你只需启动所有任务,然后让线程池处理其余部分(尽管存在一些潜在问题 - 你无法在.NET中创建自定义线程池,可能会出现线程池饥饿的问题)。

Note that you can create a custom task scheduler - see the LimitedConcurrencyLevelTaskScheduler from TaskScheduler docs.

请注意,你可以创建自定义任务调度器 - 请参阅TaskScheduler文档中的LimitedConcurrencyLevelTaskScheduler

英文:

> I limited the MaxDegreeOfParallelism to 3, you can see that a new group of 3 tasks is activated only after the first 3 are finished, although it was possible to start them immediately when the first three entered the delay.

No, it is not possible from the Parallel.For standpoint of view. MaxDegreeOfParallelism means that it will have at most 3 actions executing in parallel no matter what they are doing.

Also note Parallel.For is not Task-aware (so no fancy async-await stuff handling) and Task.WaitAll is effectively a blocking call which for example does not allow returning of the executing thread into the thread pool.

> As far as I know, Windows behaves with threads, that when one sleeps, Windows switches to run another thread. Similarly, I also need tasks.

That is handled by the ThreadPool. Essentially this was one the goals of async-await introduction - to easily handle IO-bound operations allowing to reuse the threads to do meaningful work while doing the IO waiting. You can just start all of your tasks and let the thread pool handle the rest (though there are some potential issues - you can't create a custom thread pool in .NET and you can end up with thread pool starvation).

Note that you can create a custom task scheduler - see the LimitedConcurrencyLevelTaskScheduler from TaskScheduler docs.

答案2

得分: 2

以下是已翻译的代码部分:

如果您能够找到所有待机/等待/休眠操作的相应异步 API,您可以使用 [`ConcurrentExclusiveSchedulerPair`][1] 来实现期望的行为。例如,阻塞的 `Task.WaitAll(Task.Delay(5000))` 可以被替换为异步的 `await Task.Delay(5000)`。以下是一个示例:

```csharp
using System;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using System.Diagnostics;

class Program
{
    static void Main(string[] args)
    {
        TaskScheduler scheduler = new ConcurrentExclusiveSchedulerPair(
            TaskScheduler.Default, maxConcurrencyLevel: 3).ConcurrentScheduler;

        TaskFactory factory = new TaskFactory(scheduler);

        Stopwatch stopwatch = Stopwatch.StartNew();

        Task[] tasks = Enumerable.Range(1, 12).Select(i => factory.StartNew(async () =>
        {
            Thread.Sleep(5000); // 这会计入 maxConcurrencyLevel
            Console.WriteLine($"Task: {i}, Elapsed: {stopwatch.Elapsed}");
            await Task.Delay(5000); // 这不会计入 maxConcurrencyLevel
        }).Unwrap()).ToArray();

        Task.WaitAll(tasks);
    }
}

在线演示

这可以工作,因为 TaskScheduler 不了解异步维度。它们只知道如何执行使用线程的代码,而异步操作通常[在线程之上运行][4]。

3: https://stackoverflow.com/questions/15428604/how-to-run-a-task-on-a-custom-taskscheduler-using-await "How to run a Task on a custom TaskScheduler using await?"
[4]: https://blog.stephencleary.com/2013/11/there-is-no-thread.html


<details>
<summary>英文:</summary>

In case you are able to find corresponding asynchronous APIs for all your standby/wait/sleep operations, you could achieve the desirable behavior by using a [`ConcurrentExclusiveSchedulerPair`][1]. For example the blocking `Task.WaitAll(Task.Delay(5000))` can be replaced by the asynchronous `await Task.Delay(5000)`. Here is a demo:

using System;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using System.Diagnostics;

class Program
{
static void Main(string[] args)
{
TaskScheduler scheduler = new ConcurrentExclusiveSchedulerPair(
TaskScheduler.Default, maxConcurrencyLevel: 3).ConcurrentScheduler;

    TaskFactory factory = new TaskFactory(scheduler);

    Stopwatch stopwatch = Stopwatch.StartNew();

    Task[] tasks = Enumerable.Range(1, 12).Select(i =&gt; factory.StartNew(async () =&gt;
    {
        Thread.Sleep(5000); // This counts towards the maxConcurrencyLevel
        Console.WriteLine($&quot;Task: {i}, Elapsed: {stopwatch.Elapsed}&quot;);
        await Task.Delay(5000); // This doesn&#39;t count towards the maxConcurrencyLevel
    }).Unwrap()).ToArray();

    Task.WaitAll(tasks);
}

}


[Online demo][2].

This works because `TaskScheduler`s [are not aware][3] of the asynchronous dimension. They only know how to execute code that uses threads, and asynchronous operations typically [fly above threads][4].

  [1]: https://learn.microsoft.com/en-us/dotnet/api/system.threading.tasks.concurrentexclusiveschedulerpair
  [2]: https://dotnetfiddle.net/IOboRI
  [3]: https://stackoverflow.com/questions/15428604/how-to-run-a-task-on-a-custom-taskscheduler-using-await &quot;How to run a Task on a custom TaskScheduler using await?&quot;
  [4]: https://blog.stephencleary.com/2013/11/there-is-no-thread.html

</details>



huangapple
  • 本文由 发表于 2023年4月10日 22:27:35
  • 转载请务必保留本文链接:https://go.coder-hub.com/75977966.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定