1

我已经实现了一个 SqlListener 类,它使用 SqlDependency 来等待 SQL 数据库的变化。在我的业务工作流程中,我需要等待数据库中出现一条记录。当找到请求的记录时,SqlListener 会触发一个事件。这工作正常。我可以通过进入 While 循环并等到我检测到返回的事件来使其工作。但这不是理想的设计。它使处理器徒劳地旋转很多。

我想以更聪明的方式等待事件。我阅读了很多关于使用 Task、NotificationDelegate、ManualResetEvent 等的建议......但我无法将它们全部放在一起。

一个简化的例子可能会让它更容易理解。这是我当前有效的设置。但如果可能的话,我想摆脱丑陋的 while 循环。

private const int MaxWaitTime = 5;
private SqlListener<RecordType> _recordListener;
private RecordType _record;

/// <summary>
/// Request a record and wait until it is found.
/// </summary>
public RecordType GetRecordAwait(int requestedId)
{
    // Initiate listening for record
    _recordListener = new SqlListener<RecordType>();
    _recordListener.SqlModified += SqlListener_SqlModified;
    _recordListener.StartListening(requestedId);

    // Wait until record is found
    var startTime = DateTime.Now;
    while (_record == null && 
                 DateTime.Now.Subtract(startTime).TotalSeconds < MaxWaitTime)
    {
        Thread.Sleep(1);
    }
    // Stop listening
    _recordListener.SqlModified -= SqlListener_SqlModified;
    _recordListener.Dispose();
    _recordListener = null;

    // Return record
    return _record;
}

private void SqlListener_SqlModified(object sender, SqlModifiedArgs args)
{
    _record = (RecordType)args.Record;
}
4

2 回答 2

0

您可以使用 Timer 和事件,而不是使用 While。就像是:

public class ListenerWaiting
{
    public ListenerWaiting(int waitingTimeSeconds)
    {
        _waitSeconds = waitingTimeSeconds;
    }

    private int _waitSeconds;
    private System.Timers.Timer _timer;
    private Listener _listener;

    public event EventHandler<string> ListenerDone;

    public void Listen(int listeningPeriodSeconds)
    {
        _listener = new Listener(listeningPeriodSeconds * 1000);
        _listener.ListenerCompleted += ListenerListenerCompleted;
        _timer = new System.Timers.Timer(_waitSeconds * 1000) {Enabled = true};
        _timer.Elapsed += TimerElapsed;
    }

    void ListenerListenerCompleted(object sender, string e)
    {
        StopTimer();
        StopListener();
        if (ListenerDone != null)
            ListenerDone(this, "Waiting success! Message was: " + e);
    }

    void TimerElapsed(object sender, System.Timers.ElapsedEventArgs e)
    {
        StopTimer();
        StopListener();
        if (ListenerDone != null)
            ListenerDone(this, "Waited longer than set, aborted waiting...");
    }

    private void StopTimer()
    {
        _timer.Stop();
        _timer.Elapsed -= TimerElapsed;
        _timer = null;
    }

    private void StopListener()
    {
        _listener.ListenerCompleted -= ListenerListenerCompleted;
        _listener = null;
    }
}

public class Listener
{
    private System.Timers.Timer _timer;
    private string _listeningPeriodSeconds;
    public event EventHandler<string> ListenerCompleted;

    public Listener(int listeningPeriodSeconds)
    {
        _listeningPeriodSeconds = listeningPeriodSeconds.ToString();
        _timer = new System.Timers.Timer(listeningPeriodSeconds) { Enabled = true };
        _timer.Elapsed += TimerElapsed;
    }

    private void TimerElapsed(object sender, System.Timers.ElapsedEventArgs e)
    {
        _timer.Elapsed -= TimerElapsed;
        _timer = null;
        if (ListenerCompleted != null)
            ListenerCompleted(this, _listeningPeriodSeconds);
    }

}

...然后使用它:

    static void Main(string[] args)
    {
        var wait = new ListenerWaiting(5);
        wait.ListenerDone += WaitListenerDone;

        wait.Listen(3);

        Console.ReadLine();
    }

    static void WaitListenerDone(object sender, string e)
    {
        Console.WriteLine(e);
    }

我想我可以为类找到更好的名称,但你会明白的;)

于 2013-05-08T09:51:40.387 回答
0

事实上,解决方案比我最初想象的要简单。当我改写我的问题并再次搜索时,我找到了它。我的问题中已经提到的 ManualResetEvent 原来是解决它的最简单方法。

我所要做的就是添加一个 ManualResetEvent 并将其设置为等待 ;-)

    private const int MaxWaitTime = 5000;
    private SqlListener<RecordType> _recordListener;
    private RecordType _record;
    private readonly ManualResetEvent _recordWaiter = new ManualResetEvent(false);


    /// <summary>
    /// Request a record and wait until it is found.
    /// </summary>
    public RecordType GetRecordAwait(int requestedId)
    {
        // Initiate listening for record
        _recordListener = new SqlListener<RecordType>();
        _recordListener.SqlModified += SqlListener_SqlModified;
        _recordListener.StartListening(requestedId);

        // Wait synchronously until record is found
        _recordWaiter.WaitOne(MaxWaitTime);

        // Stop listening
        _recordListener.SqlModified -= SqlListener_SqlModified;
        _recordListener.Dispose();
        _recordListener = null;

        // Return record
        return _record;
    }

    private void SqlListener_SqlModified(object sender, SqlModifiedArgs args)
    {
        _record = (RecordType)args.Record;
        _recordWaiter.Set();
    }
于 2013-05-13T08:50:27.730 回答