1

I have a program doing long running tasks, which should be started at program startup and it should be possible to be restarted in any moment after that.

My purpose is if "restart" is called, this to happen:

  • Request for all tasks to be finished
  • Waiting for all tasks to be completed
  • All functionalities are started again

I want to use async/await and still lock the process of starting all tasks in order to be sure that not a single restart is proceed until previous start/restart is finished.

As i saw for many reasons async/await does not work with lock statemnts, so I end up using SemaphoreSlim which works great for me. here is my code:

private readonly SemaphoreSlim m_semaphoreSlim;
private CancellationTokenSource m_cancellationTokenSource;
private CancellationToken m_cancellationToken;

public FeedClientService(IList<IFeedConfigurationBuilder> feedConfigs)
{
    m_semaphoreSlim = new SemaphoreSlim(1, 1);
    m_feedConfigs = feedConfigs;
}

public void Start()
{
    Task.Run(() => this.FetchFeeds());
}

public void Restart()
{
    if (m_cancellationTokenSource != null) m_cancellationTokenSource.Cancel();

    Task.Run(() => this.FetchFeeds());
}

private async Task FetchFeeds()
{
    try
    {
        await m_semaphoreSlim.WaitAsync();

        m_cancellationTokenSource = new CancellationTokenSource();
        m_cancellationToken = m_cancellationTokenSource.Token;

        Task[] tasks = new Task[m_feedConfigs.Count];
        for (int i = 0; i < m_feedConfigs.Count; i++)
        {
            var index = i;
            tasks[index] = Task.Run(async () => await this.FetchFeed(index), m_cancellationToken);
        }

        await Task.WhenAll(tasks);
    }
    finally
    {
        m_semaphoreSlim.Release();
    }          
}

As it is pointed here https://stackoverflow.com/a/4154526/4664866 - "The SemaphoreSlim class represents a lightweight, fast semaphore that can be used for waiting within a single process when wait times are expected to be very short". I haven't found any source where is specified what means "very short" and I'm not sure if my code will not have performance bottlenecks, because the tasks I am starting are for sure not short running ones.

TL;DR;

  • What means "very short waiting times"?
  • What are alternative in case waiting times are very long?
Antoan Elenkov
  • 711
  • 1
  • 5
  • 20
  • If waiting times are long, then the performance characteristics of "acquiring the lock" should be insignificant in comparison, right? – spender Dec 05 '18 at 16:44
  • 1
    IIRC this "short waiting times" only applies to synchronous `Wait`, because in this case, the SemaphoreSlim goes into a spin-wait, which will eat up CPU if wait times extend past a few cycles. I believe that `WaitAsync` does not have this spin-wait behaviour. – spender Dec 05 '18 at 16:53
  • 1
    @spender you are absolutely right. I checked imlementation of "Wait" and "WaitAsync methods".As you pointed, SpinWait is used only in "Wait". Thanks! – Antoan Elenkov Dec 05 '18 at 21:53
  • But to answer your original question: it depends. The amount of spinning is proportional to the number of cores on your CPU. The only way to know whether it's helping or hurting in your case is to benchmark and/or profile your code – Kevin Gosse Dec 06 '18 at 07:16

1 Answers1

4

It turned out that SemaphoreSlim.WaitAsync is not using spin-wait technique at all(for reference - implementation of SemaphoreSlim). So locking resources, even with long running tasks in them, will not affect CPU consumption.

Antoan Elenkov
  • 711
  • 1
  • 5
  • 20