SemaphoreSlim取消令牌

时间:2014-06-07 00:23:13

标签: c# .net async-await semaphore cancellation

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();
    }


}

我试图模仿一个启动无限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

1 个答案:

答案 0 :(得分:2)

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

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

    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();
            }
        }
    }
    

    我已经将Task.Delay分开,使其更加相似,在我看来,这是一个真实的情况,你有几个不同的操作(例如下载,解析,保存),你想查询之间的取消令牌那些步骤。

相关问题