Telegram - GetUpdates Process
Disposable

You class manages a Timer, which you dispose of if Terminate is called.  It feels like your class should implement IDisposable and cleanup the timer if it hasn't already been done.

Is Started

Does this need to be exposed as a public property?  You're setting it in both your Start and Stop methods, but you never check its existing state.  Is calling Start twice in a row without calling Stop acceptable?  Perhaps Start should return IsStarted, rather than a value to indicate if it was able to acquire the lock.

Uncertain Usage

As I said in my response to your previous code it is unclear what your use case for this code is.  You are protecting the list, by creating a copy of it in GetLastUpdates, however if this is called by multiple threads each thread could end up with its own copy of the underlying list with the same items in it.  Each thread could then decide to perform some processing on the same item in the list.  Does this matter?  Is it prevented in someway outside of the code you've shown?  Some alternative options would be:


Calling threads simply pop the first item from the list, then perform processing on it.  If the underlying _updateList was a ConcurrentQueue and it was the the only way to fetch items, you could change GetUpdates to enqueue items and probably do away with the Monitor lock altogether.  Using your existing data structure you would have something like this.

public Update GetFirstItem()
{
    Update update = null;

    if (Monitor.TryEnter(_lock))
    {
        try
        {
            update = _updatesList.FirstOrDefault();
            if (null != update)
            {
                _updatesList.Remove(update);
            }
        }
        finally
        {
            Monitor.Exit(_lock);
        }
    }
    return update;
}

Calling threads pass in a selection delegate to select the item from the list they need to remove, then perform processing on it.

public Update GetFirstMatchingUpdate(Func&lt;Update, bool&gt; searchCriteria)
{
    Update update = null;

    if (Monitor.TryEnter(_lock))
    {
        try
        {
            update = _updatesList.Where(searchCriteria).FirstOrDefault();
            if(null != update)
            {
                _updatesList.Remove(update);
            }
        }
        finally
        {
            Monitor.Exit(_lock);
        }
    }
    return update;
}


Called like this:

var update = au.GetFirstMatchingUpdate(x =&gt; x.SomeField == "someText" &amp;&amp; x.Id &gt; 3);

Calling threads call existing GetLastUpdates, find the update they want to process, remove it from the list, then perform processing only if that removal works (i.e. they were the first to remove it).
Processing is only actually performed by a single thread (with the contention being between the update and the processor), in which case the problem I've described doesn't exist.