2

我目前正在制作一个程序来模拟视觉 C# 中的一组 ATM。如果已经从其他位置访问过它,它应该阻止某人访问他们的帐户。是否可以在信号量等待时显示该帐户已被访问的消息?

这是使用信号量的代码部分:

private void button1_Click(object sender, EventArgs e)
        {
            count++;
            if (count == 1)
            {
                account = findAccount();
                if (findAccount() != 5)
                {
                    textBox1.Text = "Please Enter Your Pin";

                }
                else
                {
                    textBox1.Text = "Please Enter Your Account Number";
                    count = 0;
                }
                textBox2.Clear();
            }
            if (count == 2)
            {
                if (findPin(account) == true)
                {
                    semaphore.WaitOne();
                    textBox1.Text = "1: Take Out Cash \r\n2: Balance \r\n3: Exit";
                }
                else
                {
                    semaphore.Release();
                    textBox1.Text = "Please Enter Your Account Number";
                    count = 0;
                }
                textBox2.Clear();
            }
            if (count == 3)
            {
                atm();
            }
            if (count == 4)
            {
                withdraw();
            }
            if (count == 5)
            {
                int value = Convert.ToInt32(textBox2.Text);
                customWithdrawl(value);
            }

        }
4

2 回答 2

2

考虑对WaitOne进行两次调用。第一次调用的超时时间为零并返回一个布尔值,它会告诉您是否获得了信号量,或者其他人是否仍然拥有它。从那里可能会发生两件事:

1)如果其他人拥有它,弹出一条消息说“其他人拥有信号量”并再次调用WaitOne,但没有超时(就像你现在正在做的那样)。对 WaitOne 的第二次调用返回后,关闭一秒钟前弹出的窗口。

2) 如果您对 0 超时的 waitOne 调用返回 true,那么您在第一次尝试时就获得了信号量。无需弹出窗口。

例子:

if( semaphore.WaitOne(0) ) //This returns immediately
{
    //We own the semaphore now.
    DoWhateverYouNeedToDo();
}
else
{   
    //Looks like someone else already owns the semaphore.
    PopUpNotification();
    semaphore.WaitOne(); //This one will block until the semaphore is available
    DoWhateverYouNeedToDo();
    CloseNotification();
}

semaphore.Release();

请注意,这里还隐藏着一些其他问题。

  1. 您可能希望使用 try/finally 块来释放信号量,以确保它在所有异常路径中都被释放。
  2. 从 GUI 线程调用 semaphore.WaitOne() 也可能是个坏主意,因为应用程序在等待时将变得无响应。事实上,PopUpNotification()如果在执行第二次等待时挂起 GUI 线程,您可能看不到结果。考虑在第二个线程上进行长时间等待,并在拥有信号量后在 GUI 线程上引发事件

考虑以下设计来解决问题 2:

private void button1_Click(object sender, EventArgs e)
{
    if(AcquireSemaphoreAndGenerateCallback())
    {
        //Semaphore was acquired right away.  Go ahead and do whatever we need to do
        DoWhateverYouNeedToDo();
        semaphore.Release()
    }
    else
    {
        //Semaphore was not acquired right away.  Callback will occur in a bit
        //Because we're not blocking the GUI thread, this text will appear right away
        textBox1.Text = "Waiting on the Semaphore!";

        //Notice that the method returns right here, so the GUI will be able to redraw itself
    }
}

//This method will either acquire the semaphore right away and return true, or
//have a worker thread wait on the semaphore and return false.  In the 2nd case,
//"CallbackMethod" will run on the GUI thread once the semaphore has been acquired
private void AcquireSemaphoreAndGenerateCallback()
{
    if( semaphore.WaitOne(0) ) //This returns immediately
    {
        return true; //We have the semaphore and didn't have to wait!
    }
    else
    {
        ThreadPool.QueueUserWorkItem(new WaitCallback(Waiter));
        return false; //Indicate that we didn't acquire right away
    }
}

//Wait on the semaphore and invoke "CallbackMethod" once we own it.  This method
//is meant to run on a background thread.
private void Waiter(object unused)
{
    //This is running on a separate thread
    Semaphore.WaitOne(); //Could take a while

    //Because we're running on a separate thread, we need to use "BeginInvoke" so
    //that the method we're calling runs on the GUI thread
    this.BeginInvoke(new Action(CallbackMethod));
}

private void CallbackMethod()
{
    textBox1.Text = string.Empty; //Get rid of the "Waiting For Semaphore" text.  Can't do this if we're not running on the GUI thread
    DoWhateverYouNeedToDo();
    semaphore.Release();
}

现在,这种解决方案也可能充满危险。跟踪程序的执行有点困难,因为它从一个方法跳到另一个方法。如果您有异常,则可能很难从中恢复并确保您的所有程序状态都是正确的。您还必须通过所有这些方法调用来跟踪帐号和密码等内容。为了做到这一点,Waiter 和 CallbackMethod 可能应该采用一些参数来跟踪传递到每个步骤的状态。也没有办法中止等待(超时)。它可能会起作用,但不应将其放入任何生产代码中,因为它太难以维护或扩展。

如果你真的想把它做对,你应该考虑将 ATM 逻辑封装在一个对象中,该对象将引发 GUI 可以订阅的事件。你可以有一个ATM.LogInAsync(Account,Pin)可以调用的方法。此方法将立即返回,但一段时间后,ATM 类上的一个事件(如“LogInComplete”)将触发。此事件将有一个自定义 EventArgs 对象,该对象将包含用于跟踪发生了哪些登录的数据(主要是帐号)。这称为基于事件的异步模式

或者,如果您使用的是 C# 5.0,则可以在方法中使用新的Async/Await语法AcquireSemaphoreAndGenerateCallback()。这可能是最简单的方法,因为编译器将为您处理大部分复杂性

于 2013-03-20T17:07:52.477 回答
1

是的,您可以在 Wait 方法之前显示您的消息/表单/消息框。然后当它收到解除封锁的信号时,你隐藏你的信息。

于 2013-03-20T17:04:40.187 回答