1

I have a public string called simply m. The code shall only proceed when m is not null anymore. As long as m is null it should just wait. Any suggestions on how to do that? ( Without pausing the entire program, otherwise m would never change)

LuaEden X
  • 21
  • 1
  • 2

5 Answers5

3

You can use a TaskCompletionSource<TResult>.

Create a new instance, and anywhere that needs to wait for the string to be available, you can use await tcs.Task. When whatever produces the string finishes, you call tcs.SetResult(result).

This allows your code to avoid busy-loops, as one other answer suggested, and if you have a highly concurrent application, it avoids blocking threads, as another answer suggested.

zivkan
  • 12,793
  • 2
  • 34
  • 51
2

How about turning your "public string" into a property?

private string _m
public string M
{
    get => _m;
    set 
    {
        if (value != _m)
        {
            _m = value;
            if (string.IsNullOrEmpty(value))
            {
                DoSomething(value);
            }
        }
    }
}
  • This really is the best way to do it. However, if `DoSomething` is doing something time consuming, or making an I/O request of some sort, it would be better to just schedule it for later and have some other job pick it up. – Gabriel Luci Dec 27 '19 at 13:26
  • To me the only sensible interpretation of the question is that we're working in a multi-threaded environment, so either this doesn't answer the question or the question is unclear. – V0ldek Dec 28 '19 at 21:52
  • @V0ldek the question is unclear and OP hasn't provided any additional info yet. This code could work with multi-thread anyways.For example Another thread could be fetching `M` asynchronously while `DoSomething()` could be running on main thread updating UI. – Coding Enthusiast Dec 29 '19 at 16:35
  • @CodingEnthusiast There's an obvious race condition on multiple writes. Say you do `M = "abc"` and `M = null` from two different threads. Depending on ordering you can get `DoSomething` called once, two times or not at all. – V0ldek Dec 29 '19 at 16:52
2

If you are looking for a simple async solution with a small latency:

await Task.Run(async () => { while (string.IsNullOrWhiteSpace(m)) await Task.Delay(10); });

or

while (string.IsNullOrWhiteSpace(m)) await Task.Delay(10);
Johan Donne
  • 3,104
  • 14
  • 21
0

The simplest solution in this case is write a while(true) loop and add if statement which checks string is null or not. But this will keep consuming CPU cycles unnecessarily. So I suggest you to implement conditional wait which will basically trigger your code once the condition is matched (string becomes null in your case) and will not waste CPU cycles in just waiting.

0

I am assuming we're talking about a multi-threaded environment. Refer to Condition Variables in C#/.NET. You can use the Monitor class, but you need to change your public field to a property (which you should do anyway).

private readonly string? _m;

public string? M
{
    get => _m;
    set
    {
        lock (_m)
        {
            _m = value;
            if (_m != null)
            {
                Monitor.PulseAll(_m);
            }
        }
    }
}

public void Foo()
{
    lock (_m)
    {
        while (_m == null)
        {
            Monitor.Enter(_m);
        }

        // Code using the _m resource...
    }

    // Rest of your code...
}

This will work and is enough if you have a small number of threads and locking will be rather rare. If there are multiple threads reading from and writing to _m, this might be unperformant and you might want to mix this with a ReaderWriterLockSlim](https://learn.microsoft.com/en-us/dotnet/api/system.threading.readerwriterlockslim?view=netstandard-2.1) to optimise for the common case of _m not being null.

V0ldek
  • 9,623
  • 1
  • 26
  • 57