简体   繁体   中英

Waiting for an object to be updated by another task C#

I've read over many many posts about 'waiting' for a task... I can't seem to get this straight because it may be a twist on actually waiting for a task, i'm waiting for an object to be updated and if not timeout.

I'm using redis which in a seperate message handler correctly updates a list of basic acks/nacks when it receives them from the redis server. (too much code to post and not really relevant to this question).

The code in question is how do I call the below 'WaitForAck' method, where its duty is to check if the above list gets appended with the ack/nack it's waiting for.

So I want to be able to call 'WaitForAck' anywhere where it will wait for (7 seconds) if the list 'dic_DEAL_OUTPUTRESP_MessageQueue' gets appended by the seperate redis handler, basically blocking the user from moving forward but don't block any other threads such as the redis message handler thread waiting for its update.

I suppose i'm a bit confused if this should be any kind of await Task because I don't want it to be async? I want the user to wait 7 seconds to see if it receives an ack/nack (list has a value) otherwise it goes on to notify user of a timeout (handled seperately).

Here is my 'WaitForAck' method:

    public static void WaitForAck(string origUuid)
    {
        DateTime dt = DateTime.Now;

        Cursor.Current = Cursors.WaitCursor;
        try
        {
            while (dic_DEAL_OUTPUTRESP_MessageQueue[origUuid] == null)
            {
                Debug.WriteLine("Waiting for Redis Ack");
                Task.Delay(1000);
                TimeSpan ts = DateTime.Now - dt;
                if (ts.TotalSeconds > iWAIT)
                {
                    break;
                }
            }
        }
        catch { }
        Cursor.Current = Cursors.Default;
    }

Thank you in advance

EDIT: Added Call

 private async void btnEnter_Click(object sender, EventArgs e)
    {
        // Process trade entry / amendment
        if (ConfirmEntries() != 0)
        {
            this.DialogResult = DialogResult.None;
            return;
        }
        DefineDeal();

        // Attempt Commit
        DEAL_REQ dEAL_REQ_ADD = new DEAL_REQ();
        dEAL_REQ_ADD.msgTimestamp = ReturnUTCTime(DateTime.Now).ToString(sDATETIME_FORMATMS);
        dEAL_REQ_ADD.uuid = Guid.NewGuid().ToString();
        dEAL_REQ_ADD.commandKey = sMeFunction == sADD ? sDEAL_REQ_ADD : sDEAL_REQ_UPDATE;
        dEAL_REQ_ADD.listenChannel = sRPC_CHANNEL_OUTPUT;
        dEAL_REQ_ADD.payload = _fxTempDeal;
        // Build json msg
        string sJson = JsonConvert.SerializeObject(dEAL_REQ_ADD);
        // Publish request
        // Prime response queue with guid in key
        dic_DEAL_OUTPUTRESP_MessageQueue.Add(dEAL_REQ_ADD.uuid, null);
        //Task.Delay(100);
        IDatabase db = redisConn.GetDatabase(redisDB);

        db.ListLeftPush(sRPC_CHANNEL_INPUT, sJson);
        //Task.Delay(100);
        //string s = db.ListLeftPop(sRPC_LB_INPUT);

        // Wait for ack 
        var value = await this.WaitForAck2(dEAL_REQ_ADD.uuid);

        // Received ack/nack
        if (value != null)
        {
            DEAL_RESP rESP = dic_DEAL_OUTPUTRESP_MessageQueue[dEAL_REQ_ADD.uuid];

            if (rESP.commandKey == sDEAL_REQ_ADD_ACK || rESP.commandKey == sDEAL_REQ_UPDATE_ACK)
            {
                this.DialogResult = DialogResult.OK;
            }
            else if (rESP.commandKey == sDEAL_REQ_ADD_NACK || rESP.commandKey == sDEAL_REQ_UPDATE_NACK)
            {
                MessageBox.Show("Unable to " + sMeFunction + " this deal. Please contact support. Reason : " + rESP.payload.display_msg, sAPP_NAME, MessageBoxButtons.OK, MessageBoxIcon.Error);
                this.DialogResult = DialogResult.Abort;
            }
        }
        // Timed out
        else
        {
            MessageBox.Show("Unable to " + sMeFunction + " this deal. Please contact support. Reason : Request Timeout", sAPP_NAME, MessageBoxButtons.OK, MessageBoxIcon.Error);
            dic_DEAL_OUTPUTRESP_MessageQueue.Remove(dEAL_REQ_ADD.uuid);
            dic_DEAL_TimedOut_MessageQueue.Add(dEAL_REQ_ADD.uuid, null);
            this.DialogResult = DialogResult.Cancel;
        }
    }

    public async Task<DEAL_RESP> WaitForAck2(string origUuid)
    {
        Stopwatch timer = new Stopwatch();
        timer.Start();

        while (timer.ElapsedMilliseconds < (7 * 1000))
        {
            if (await this.sync.WaitAsync(TimeSpan.FromSeconds(7)))
            {
                try
                {
                    if (dic_DEAL_OUTPUTRESP_MessageQueue.TryGetValue(origUuid, out var value))
                    {
                        return value;
                    }
                }
                finally
                {
                    this.sync.Release();
                }
            }

            await Task.Delay(1000);
        }

        return null;
    }

I think you're getting push back on your question because context of the threading requirements are not clear. If this was simply two threads running in a service then a simple autoresetevent could be used to signal that new messages have been added to the queue and then that thread could respond. But from your example it seems - due to the Cursor class usage - you're running in the UI thread which complicates it. You can't block the UI thread.

I suspect there are better UI patterns to handle this than polling the queue for 7 seconds. But if that is what fits with your current design then I think the following code would enable the async polling. I tested this using a simple console app and the threading works as expected. The outer class is not shown..

class MyClass 
{
    SemaphoreSlim sync = new SemaphoreSlim(1, 1);
    Dictionary<string, string> messages = new Dictionary<string, string>();

    public async void OnButtonClicked(EventArgs sender)
    {
        string origUuid = string.Empty; // get key from button click?

        var value = await this.WaitForAck(origUuid);

        if (null != value)
        {
            /// display it somehow...
            Console.WriteLine(value);
        }
    }

    public async Task<string> WaitForAck(string origUuid)
    {
        Stopwatch timer = new Stopwatch();
        timer.Start();

        while (timer.ElapsedMilliseconds < (7 * 1000))
        {
            if (await this.sync.WaitAsync(TimeSpan.FromSeconds(7)))
            {
                try
                {
                    if (this.messages.TryGetValue(origUuid, out var value))
                    {
                        return value;
                    }
                }
                finally
                {
                    this.sync.Release();
                }
            }

            await Task.Delay(1000);
        }

        return null;
    }

    public async Task PushAck(string origUuid, string value)
    {
        await this.sync.WaitAsync();

        try
        {
            this.messages.Add(origUuid, value);
        }
        finally
        {
            this.sync.Release();
        }
    }
}

class Program
{
    static async Task Main(string[] args)
    {
        var myform = new MyClass();

        var pub = Task.Run(() =>
        {
            int index = 0;

            while (true)
            {
                Task.Delay(1000).Wait();

                myform.PushAck(index.ToString(), "gotit").Wait();

                ++index;
            }
        });

        Stopwatch timer = new Stopwatch();
        timer.Start();

        var mymessage = await myform.WaitForAck("1");
        timer.Stop();

        Console.WriteLine($"message \"{mymessage}\" recieved in {timer.ElapsedMilliseconds} ms");
    }

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM