3

There is this class unit that has a property bool status that marks whether a method, request, should be called on the unit. I have my other class, and in it, there is a method that should call request. To avoid blocking the main thread, I want to call the method asynchronously. The problem is that there isn't an event for the status change, and I don't want to make my asynchronous call do ugly stuff like:

while(!status){}unit.request(args);

or

while(!status){Thread.Sleep(100)}unit.request(args);

especially when I do not know the timescale in which status turns true.

How do I do this?

update: i forgot to mention that i cannot change unit. sorry for that.

resgh
  • 974
  • 2
  • 12
  • 22
  • If the class does not expose an event or WaitHandle, there isn't much that you can do. – SLaks Aug 19 '12 at 14:07
  • so theres no solution to my problem... thankyou for your help! btw who downvoted me... – resgh Aug 19 '12 at 14:16
  • @idiotretard Since you may not be able to modify the code which changes the status, I've added a solution based on polling. – Asti Aug 19 '12 at 14:31

4 Answers4

2

You want to call a function (be it asynchronously or not) when a property changes. You have two choices:

  1. Attach to an even that is signalled when the property changes
  2. Periodically check the value of the property

You can't do the first, so you must do the second.

Adam Robinson
  • 182,639
  • 35
  • 285
  • 343
2

This is a sample of how you can manage this using an event.

Suppose this is your class

public class Unit
{
    private readonly object _syncRoot = new object();
    private bool _status;

    public event EventHandler OnChanged;    

    public bool Status
    {
        get
        {
            lock (_syncRoot)
            {
                return _status;    
            }
        }
        set
        {
            lock (_syncRoot)
            {
                _status = value;
                if (_status && OnChanged != null)
                {
                    OnChanged.Invoke(this, null);        
                }
            }
        }
    }

    public void Process()
    {
        Thread.Sleep(1000);
        Status = true;
    }
}

Here is how you can use it

class Program
{
    static void Main(string[] args)
    {
        var unit = new Unit();
        unit.OnChanged += Unit_OnChanged;
        Console.WriteLine("Before");
        Task.Factory.StartNew(unit.Process);
        Console.WriteLine("After");

        Console.WriteLine("Manual blocking, or else app dies");
        Console.ReadLine();
    }

    static void Unit_OnChanged(object sender, EventArgs e)
    {
        //Do your processing here
        Console.WriteLine("Unit_OnChanged before");
        Task.Factory.StartNew(()=>
        {
            Thread.Sleep(1000);
            Console.WriteLine("Unit_OnChanged finished");
        });
        Console.WriteLine("Unit_OnChanged after");
    }
}

This outputs

Before
After
Manual blocking, or else app dies
Unit_OnChanged before
Unit_OnChanged after
Unit_OnChanged finished
oleksii
  • 35,458
  • 16
  • 93
  • 163
1

This is the classic polling problem, and there really isn't an elegant solution when polling is concerned. But we can work some functional programming in to get something which isn't a nightmare to use.

    public static CancellationTokenSource Poll(
        Func<bool> termination,
        Action<CancellationToken> onexit,
        int waitTime = 0,
        int pollInterval = 1000)
    {
        var cts = new CancellationTokenSource();
        var token = cts.Token;
        Action dispose = cts.Cancel;

        var timer = new Timer(_ =>
        {
            if (termination() || token.IsCancellationRequested)
            {
                onexit(token);
                dispose();
            }
        }, null, waitTime, pollInterval);

        dispose = timer.Dispose;
        return cts;
    }

Example:

    var condition = false;

    Poll(() => condition == true, ct => Console.WriteLine("Done!"));

    Console.ReadLine();

    condition = true;

    Console.ReadLine();
Asti
  • 12,447
  • 29
  • 38
  • 1
    seriously how different is that from "while(!status){Thread.Sleep(100)}unit.request(args);"? thanks though – resgh Aug 20 '12 at 08:31
  • btw "classic polling problem"? never heard of it. what was the problem? – resgh Aug 20 '12 at 08:40
  • @idiotretard 1. It doesn't block. 2. It polls on the ThreadPool. 3. The polling is triggered by the AppDomain timer, so there isn't a lot of context switching when checking something over time. – Asti Aug 20 '12 at 17:56
0

Use a System.Threading.AutoResetEvent instead of a bool if possible:

AutoResetEvent status = new AutoResetEvent();

In your asynchronous method, wait for it:

status.WaitOne();
unit.request(args);

Then, to signal it in your other class, call Set:

status.Set();
Ry-
  • 218,210
  • 55
  • 464
  • 476
  • He has no event to `Set` from. – Asti Aug 19 '12 at 14:34
  • @Asti: Wherever `status = true;` was before is where `status.Set();` should be. (*"in your other class"*) – Ry- Aug 19 '12 at 14:37
  • unfortunately "Use a System.Threading.AutoResetEvent instead of a bool if possible" cannot be accomplished – resgh Aug 20 '12 at 08:26
  • @idiotretard: So you can't modify the containing class and you can't modify the calling class? Then it's impossible, yes. – Ry- Aug 20 '12 at 14:42
  • i can modify the calling class though. who told you i couldnt? – resgh Aug 21 '12 at 12:58