c# asynchronously call method

2019-05-11 05:02发布

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.

4条回答
Melony?
2楼-- · 2019-05-11 05:13

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();
查看更多
劫难
3楼-- · 2019-05-11 05:27

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
查看更多
该账号已被封号
4楼-- · 2019-05-11 05:29

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();
查看更多
闹够了就滚
5楼-- · 2019-05-11 05:34

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.

查看更多
登录 后发表回答