How to make System.Timers.Timer call async function synchronously when elapsed?

Roman Kovalov 20 Reputation points
2024-03-28T10:27:40.66+00:00

Hello!

I am developing a telegram bot using .NET and I am having an issue.

I have two timers that execute functions when elapsed.

  1. First timer re-posts a message (deletes a message and then posts identical copy, this is a workaround for the fact that you can't edit messages that are older then 24 hours in telegram, this functionality keeps messages always fresh and editable).
  2. Second timer just edits the message if needed.

The methods that are called when these timers elapse are marked async. This is how I attach them to elapsed event:

timer1.Elapsed += async (source, eventArgs) => { await Repost(); };
timer2.Elapsed += async (source, eventArgs) => { await Edit(); };

The problem happens when Elapsed events of these timers happen at the same time. Even tho I await inside of the lambda, methods Repost and Edit seem to be executed at the same time in the different threads asynchronously: Repost() deletes the message, and then before message copy is created Edit is trying to edit the deleted message, and I get exception.

Clearly I misunderstand en effect of await inside of the lambda and what is going on in general. My ultimate goal is to call these async methods Repost() and Edit() synchronously, so that there is no conflict.

Thanks you for your time, this is my first encounter with asynchronous programming and I will be glad to hear any explanations or advice on what am I doing or what I should do.

Thank you!

.NET
.NET
Microsoft Technologies based on the .NET software framework.
3,375 questions
C#
C#
An object-oriented and type-safe programming language that has its roots in the C family of languages and includes support for component-oriented programming.
10,249 questions
{count} votes

Accepted answer
  1. Viorel 112.1K Reputation points
    2024-03-28T17:27:49.0566667+00:00

    Try something like this:

    static readonly SemaphoreSlim s = new( 1, 1 );
    . . .
    
    timer1.Elapsed += async ( source, eventArgs ) => { await s.WaitAsync( ); try { await Repost( ); } finally { s.Release( ); } };
    timer2.Elapsed += async ( source, eventArgs ) => { await s.WaitAsync( ); try { await Edit( ); } finally { s.Release( ); } };
    
    1 person found this answer helpful.

0 additional answers

Sort by: Most helpful