SemaphoreSlim取消令牌

cub*_*nyc 6 .net c# semaphore cancellation async-await

class Program
{
    static IEnumerable<site> list = Enumerable.Range(1, 10).Select(i => new site(i.ToString()));

    static void Main(string[] args)
    {
        startup();
        Console.ReadKey();
    }

    static public void startup()
    {
        router.cts = new CancellationTokenSource();

        foreach (var s in list)
        {
            update(s);
        }
    }

    async static public void update(site s)
    {
        try
        {
            while (true)
            {
                await s.Refresh();
                if (site.count % 4 == 0)
                {
                    Console.WriteLine("Reseting Queue");
                    router.cts.Cancel();
                }
            }
        }
        catch (OperationCanceledException)
        {
            Console.WriteLine("Canceled");
            startup();
        }
    }
}

class router
{
    public static SemaphoreSlim ss = new SemaphoreSlim(1);
    public static CancellationTokenSource cts { get; set; }


}

class site
{
    public static int count = 0;
    public string sitename {get; set;}

    public site(string s)
    {
        sitename = s;
    }

    async public Task Refresh()
    {
        await router.ss.WaitAsync(router.cts.Token);
        //Console.WriteLine("{0}:: Start Refreshing ", sitename);
        await Task.Delay(1000);

        Console.WriteLine("{0}:: Done Refreshing ", sitename);
        count++;
        router.ss.Release();
    }


}
Run Code Online (Sandbox Code Playgroud)

我试图模仿一个模式,启动一个无限的while循环,模拟网站的不断更新.我用模数模仿这个.从理论上讲,我希望这可以取消信号量排队的所有任务,并从头开始重新启动队列,但似乎并没有这样做.有人可以评论我的逻辑和模式吗?

现在的输出看起来像这样::

1:: Done Refreshing
2:: Done Refreshing
3:: Done Refreshing
4:: Done Refreshing
Reseting Queue
Canceled
Canceled
Canceled
Canceled
Canceled
Canceled
Canceled
Canceled
5:: Done Refreshing
1:: Done Refreshing
2:: Done Refreshing
3:: Done Refreshing
Reseting Queue
Canceled
Canceled
Canceled
Canceled
Canceled
Canceled
Canceled
Canceled
Canceled
Canceled
Canceled
Canceled
Canceled
Canceled
4:: Done Refreshing
5:: Done Refreshing
6:: Done Refreshing
7:: Done Refreshing
Reseting Queue
Canceled
Canceled
Canceled
Canceled
Canceled
Canceled
Canceled
Canceled
Canceled
Canceled
Canceled
Canceled
Canceled
Canceled
8:: Done Refreshing
9:: Done Refreshing
10:: Done Refreshing
1:: Done Refreshing
Reseting Queue
Canceled
Run Code Online (Sandbox Code Playgroud)

i3a*_*non 5

所以,我有几条评论,有些是错误,有些只是建议:

  1. 如果可以,我更喜欢检查取消和结束操作,而不是抛出异常。
  2. updateasync void,在事件处理程序之外几乎不应该发生的事情。您无法观察到错误,它会导致无数错误。
  3. 要并行化站点更新,请触发所有更新并仅等待一次 Task.WhenAll
  4. startup当任何操作被取消时,您正在调用。这意味着当您取消 10 个站点更新中的 5 个时,您将启动 50 个新站点更新。那是不必要的。
  5. 将 a 传递CancellationTokenSemaphoreSlim.WaitAsynconly 会在等待信号量时观察到取消。任何已经运行的操作都不会停止。这真的是你的意图吗?更好的方法是在更新时检查令牌。这可以通过将令牌传递给Task.Delay操作来模拟。

这是我将如何做到的:

class Program
{
    static IEnumerable<site> list = Enumerable.Range(1, 10).Select(i => new site(i.ToString()));

    static void Main(string[] args)
    {
        Startup().Wait();
        Console.ReadKey();
    }

    static async Task Startup()
    {
        while (true)
        {
            router.cts = new CancellationTokenSource();

            await Task.WhenAll(list.Select(s => Update(s)));
        }
    }

    static Task Update(site s)
    {
        if (site.count % 4 == 0)
        {
            Console.WriteLine("Reseting Queue");
            router.cts.Cancel();
        }
        else
        {
            return s.Refresh();
        }
    }
}

class router
{
    public static SemaphoreSlim ss = new SemaphoreSlim(1);
    public static CancellationTokenSource cts { get; set; }
}

class site
{
    public static int count = 0;
    public string sitename {get; set;}

    public site(string s)
    {
        sitename = s;
    }

    public async Task Refresh()
    {
        await router.ss.WaitAsync();
        try
        {
            if (router.cts.token.IsCancellationRequested)
            {
                return;
            }
            await Task.Delay(500);

            if (router.cts.token.IsCancellationRequested)
            {
                return;
            }
            await Task.Delay(500);

            Console.WriteLine("{0}:: Done Refreshing ", sitename);
            count++;
        }
        finally
        {
            router.ss.Release();
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

Task.Delay在我看来,我已将其拆分以使其更类似于您有几个不同操作(例如下载、解析、保存)并且您想在这些步骤之间查询取消标记的真实情况。