如何编写代码以实现这些任务?

时间:2019-07-27 08:58:00

标签: c#

我想用C#实现一些异步代码,但是我不能写。给我一个提示。

我有4个任务(任务0,任务1,任务2,任务3)。

  • 任务1在任务0之后。
  • 任务3在任务2之后。
  • 任务0和任务2可以并行。
  • 任务1和任务3仅一个接一个地完成,即任务1在任务3之后或任务3在任务1之后。

然后,如何用C#代码实现这些任务?

下面的代码是不完善的,因为任务1和任务3不应是庸俗的。

static Task WholeTask()
{
    var tasks = new List<Task>();
    tasks.Add(Task0and1());
    tasks.Add(Task2and3());
    return Task.WhenAll(tasks);
}

static async Task Task0and1()
{
    await Task.Run(() => Method0());
    await Task.Run(() => Method1());
}

static async Task Task2and3()
{
    await Task.Run(() => Method2());
    await Task.Run(() => Method3());
}

static void Method0()
{
    Thread.Sleep(1000);
    Console.WriteLine("task 0 is completed");
}
static void Method1()
{
    Console.WriteLine("task 1 starts");
    Thread.Sleep(1000);
    Console.WriteLine("task 1 is completed");
}
static void Method2()
{
    Thread.Sleep(1000);
    Console.WriteLine("task 2 is completed");
}
static void Method3()
{
    Console.WriteLine("task 3 starts");
    Thread.Sleep(1000);
    Console.WriteLine("task 3 is completed");
}
static void Main(string[] args)
{
    WholeTask().Wait();
}

3 个答案:

答案 0 :(得分:1)

  

我有4个任务

我将这些定义如下,因为更容易看到答案如何工作:

Task Task0();
Task Task1();
Task Task2();
Task Task3();
  

任务1在任务0之后。任务3在任务2之后。

您可以使用await组成(链接/链式)任务:

async Task Task0and1()
{
  await Task0();
  await Task1();
}

async Task Task2and3()
{
  await Task2();
  await Task3();
}
  

任务0和任务2可以是并行的。

使用Task.WhenAll进行异步并发:

var task0and1 = Task0and1();
var task2and3 = Task2and3();
await Task.WhenAll(task0and1, task2and3);
  

任务1和任务3仅一个接一个地完成,即任务1在任务3之后或任务3在任务1之后。

SemaphoreSlim用于异步兼容锁:

private SemaphoreSlim _mutex = new SemaphoreSlim(1);
async Task Task0and1()
{
  await Task0();
  await _mutex.WaitAsync();
  try { await Task1(); }
  finally { _mutex.Release(); }
}

async Task Task2and3()
{
  await Task2();
  await _mutex.WaitAsync();
  try { await Task3(); }
  finally { _mutex.Release(); }
}

最终代码:

private SemaphoreSlim _mutex = new SemaphoreSlim(1);
async Task Task0and1()
{
  await Task0();
  await _mutex.WaitAsync();
  try { await Task1(); }
  finally { _mutex.Release(); }
}

async Task Task2and3()
{
  await Task2();
  await _mutex.WaitAsync();
  try { await Task3(); }
  finally { _mutex.Release(); }
}

async Task WholeTask()
{
  var task0and1 = Task0and1();
  var task2and3 = Task2and3();
  await Task.WhenAll(task0and1, task2and3);
}

以上内容仅在您的任务是实际的异步任务时适用。如果您的任务是同步任务,则应该使用上述同步任务。逐步,将是:

  

我有4个任务

我将这些定义如下:

void Task0();
void Task1();
void Task2();
void Task3();
  

任务1在任务0之后。任务3在任务2之后。

您可以通过依次调用来构成(链接/链)功能:

void Task0and1()
{
  Task0();
  Task1();
}

void Task2and3()
{
  Task2();
  Task3();
}
  

任务0和任务2可以是并行的。

使用Parallel或PLINQ进行并行处理。在这种情况下,由于我们要调用两个函数,因此Parallel.Invoke是自然的选择:

Parallel.Invoke(task0and1, task2and3);
  

任务1和任务3仅一个接一个地完成,即任务1在任务3之后或任务3在任务1之后。

使用lock进行互斥:

private object _mutex = new object();
void Task0and1()
{
  Task0();
  lock (_mutex) { Task1(); }
}

void Task2and3()
{
  Task2();
  lock (_mutex) { Task3(); }
}

最终代码:

private object _mutex = new object();
void Task0and1()
{
  Task0();
  lock (_mutex) { Task1(); }
}

void Task2and3()
{
  Task2();
  lock (_mutex) { Task3(); }
}

void WholeTask()
{
  Parallel.Invoke(task0and1, task2and3);
}

答案 1 :(得分:-1)

以下是解决问题所需的构建基块:

  • Chain任务0和1,以及链接任务2和3。
  • 使用lock statement保护任务1和3中的关键部分。这确保了它们不会并行执行。

答案 2 :(得分:-1)

我认为这对您有用

static void Main(string[] args)
    {
        var task0 = new Task(WorkWithSleep("task0"));
        var task1 = new Task(WorkWithSleep("task1"));
        var task2 = new Task(WorkWithSleep("task2"));
        var task3 = new Task(WorkWithSleep("task3"));


        var after0or2 = Task.WhenAny(task0, task2).ContinueWith(t =>
        {
            if (if (task0 == t.Result)
            {
                Task.WhenAll(task2, task1).ContinueWith((tt) => task3.Start());
                task1.Start();
            }
            else
            {
                Task.WhenAll(task0, task3).ContinueWith((tt) => task1.Start());
                task3.Start();
            }
        });

        task0.Start();
        task2.Start();
        var allwork = Task.WhenAll(task0, task1, task2, task3);

        allwork.Wait();
        Console.ReadLine();
    }

    public static Action WorkWithSleep(string name, int wait = 1000)
    {
        return () =>
        {
            Console.WriteLine($"Work:{name} STARTED:{DateTime.Now.ToLongTimeString()}");
            Thread.Sleep(wait); //This will simulate proccesor work
            Console.WriteLine($"Work:{name} ENDED:{DateTime.Now.ToLongTimeString()}");
        };
    }

您可以玩等待时间,看看这是否是您想要的。但这不是我所建议的,因为在进行处理器密集型工作时,可以通过编写案例特定的代码来完成许多优化,并且如果需要并行化,则可能会花费很长时间,并且每次优化都很重要。

public static async Task MainAsync()
        {
            var task0 = Task.Run(WorkWithSleep("task0",5000));
            var task2 = Task.Run(WorkWithSleep("task2"));

            var task0or2 = await Task.WhenAny(task0, task2);

            if (task0or2 == task0)
            {
                WorkWithSleep("task1")(); //No need to do a run since the thred we are on in not buisy. and can just run on the same one
                await task2;
                WorkWithSleep("task3")();
            }
            else
            {
                WorkWithSleep("task3")();
                await task0;
                WorkWithSleep("task1")();
            }
        }

编辑:修改条件后进行更改。

EDIT2:从使用可变的IsComplete变为参考检查。

EDIT3:添加了一种正确的异步方法来实现。