C#异步调用方法

3

有一个类unit,具有属性bool status,用于标记是否应在该单元上调用方法request。 我有另一个类,在其中,有一个方法应该调用request。为了避免阻塞主线程,我想异步调用该方法。问题在于没有状态更改的事件,而且我不想让异步调用做一些丑陋的事情:

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

或者

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

特别是当我不知道status变为true的时间范围时,该怎么办?

我该怎么做呢?

更新:我忘记提到我不能改变unit。对此我感到抱歉。


如果类没有公开事件或WaitHandle,则你所能做的不多。 - SLaks
所以我的问题没有解决方案...谢谢你的帮助!顺便问一下,是谁给我点了踩... - resgh
@idiotretard 由于您可能无法修改更改状态的代码,因此我已添加了基于轮询的解决方案。 - Asti
4个回答

2

如果你想在属性改变时调用一个函数(无论是异步还是同步),你有两个选择:

  1. 侦听当属性改变时发出的事件
  2. 定期检查属性值

由于第一种方法不可行,所以必须采用第二种方法。


真的没有其他解决方案吗? - resgh
@idiotretard:没有,你期望它看起来像什么? - Adam Robinson
我认为这里可能有一些天才能够做出像编写一个包装类来完成这项工作的事情。 - resgh
@AdamRobinson 我觉得这次对话很有趣。https://dl.dropbox.com/u/1928972/namecallinguserhandle.JPG - Asti

2

以下是如何使用“事件”来管理的示例。

假设这是您的类:

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;
    }
}

以下是如何使用它的方法:

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");
    }
}

这将输出

之前的
之后的
手动阻塞,否则应用程序将崩溃
Unit_OnChanged之前
Unit_OnChanged之后
Unit_OnChanged完成

通常我会喜欢这个,但问题是我不能更改单位。不过还是谢谢。 - resgh

1
这是经典的轮询问题,当涉及到轮询时,真的没有一种优雅的解决方案。但我们可以加入一些函数式编程来得到一些不太难用的东西。
    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;
    }

例子:

    var condition = false;

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

    Console.ReadLine();

    condition = true;

    Console.ReadLine();

1
这跟“while(!status){Thread.Sleep(100)}unit.request(args);”有多大区别啊?不过还是谢谢。 - resgh
顺便问一下,“经典轮询问题”是什么?我从未听说过。这个问题是什么? - resgh
  1. 它不会阻塞。
  2. 它在线程池上轮询。
  3. 轮询由应用程序域计时器触发,因此在随时间检查某些内容时没有太多的上下文切换。
- Asti

0

如果可能的话,使用System.Threading.AutoResetEvent而不是bool

AutoResetEvent status = new AutoResetEvent();

在你的异步方法中,等待它:
status.WaitOne();
unit.request(args);

然后,在您的其他类中发出信号,调用Set

status.Set();

他没有任何事件可以从中“设置”。 - Asti
@Asti:无论status = true;在哪里,status.Set();都应该在那里。(“在你的其他类中”) - Ry-
很遗憾,“如果可能,请使用System.Threading.AutoResetEvent而不是bool”无法实现。 - resgh
@idiotretard:所以你无法修改包含类和调用类?那么就不可能了,是的。 - Ry-
我可以修改调用类。谁告诉你我不能? - resgh

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接