views:

110

answers:

4

I have a problem returning an object that was created in other thread. The situation is this, my app need to send some request to remote server and wait an answer. In the main thread (1) i have a method called SendAndWait. This method put a message in a message queue for be sent and wait for an answer. Another thread (2) send the messages in the queue. A third thread (3) recieve messages and return to the main thread (1) the message with their answer.

This is my code.

Thread1:

void SendAndWait()
{
    AutoResetEvent waitForAnswer = new AutoResetEvent(false);
    int msgReferenceNumber = 1;
    MyMessage msg;

    // Register to wait for answer to the message with the number = msgReferenceNumber
    RegisterToWait(msgNumber, waitForAnswer, ref msg);
    // Send the request to the server, with the reference number.
    SendRequest(msgReferenceNumber);

    // Wait until the server send my answer
    waitForAnswer.WaitOne();

    // Now I have my  answer. BUT I HAVE msg == null
    MessageBox.Show(msg.Text);
}

Thread 2:

public class WhoWait
{
    public int RefNumMsg;
    public AutoResetEvent WaitForAnswer;
    public MyMessage MessageWithAnswer;
}
// In this Dicctionary I save who wait for answers.
private Dictionary<int, WhoWait> Waiting = new Dictionary<WhoWait>();

void RegisterToWait(int msgRefNumber, AutoResetEvent waitForAnswer, ref msgWithAnswer)
{
  WhoWait w = new WhoWait();
  w.RefNumMsg = msgRefNumber;
  w.WaitForAnswer = waitForAnswer;
  w.MessageWithAnswer = msgWithAnswer;

  Waiting.Add(msgRefNumber, w);
}

Thread3:

void OnRecieve()
{
     // Get data from server
     MyMessage msg = GetMessageData();

     // Search for someone waiting for this answer
     WhoWait w = Wainting[msg.RefNum];

     // Set the response message
     w.MessageWithAnswer = msg;

     // Warn to the main thread. Message with their answer arrive
     w.WaitForAnswer.Set();
}

The whole thing is working, except for the Message reference. When I read the object with I pass wich the ref key I have a null.

Can someone help me?

Thanks in advance.

+1  A: 

You are not giving msgWithAnswer a value, you are passing a null reference then not assigning the null reference a value.

   void RegisterToWait(int msgRefNumber, AutoResetEvent waitForAnswer, ref msgWithAnswer) 
   { 
      // msgWithAnswer = something here
      WhoWait w = new WhoWait(); 
      w.RefNumMsg = msgRefNumber; 
      w.WaitForAnswer = waitForAnswer; 
      w.MessageWithAnswer = msgWithAnswer; 

      Waiting.Add(msgRefNumber, w); 
   } 
kyndigs
+3  A: 

When you use ref to pass an argument, this lets you set the object reference in the function. When you use it like this:

w.MessageWithAnswer = msgWithAnswer;

It doesn't make w.MessageWithAnswer a copy of the reference -- it sets w.MessageWithAnswer to whatever msgWithAnswer is currently referring to. When you assign MessageWithAnswer, it won't change msgWithAnswer.

Lou Franco
Thanks, I alreadt try this. But doesn't work, because in the RegisterToWait method I have the reference, but then I lose it.
mgagna
+1  A: 

I'd use Action objects instead of references, because (as already mentioned) you can't use refs like you're attempting to. See below:

void SendAndWait()
{
    AutoResetEvent waitForAnswer = new AutoResetEvent(false);
    int msgReferenceNumber = 1;
    MyMessage msg;

    // Register to wait for answer to the message with the number = msgReferenceNumber
    RegisterToWait(msgNumber, waitForAnswer, receivedMessage => msg = receivedMessage);

    // Send the request to the server, with the reference number.
    SendRequest(msgReferenceNumber);

    // Wait until the server send my answer
    waitForAnswer.WaitOne();

    // Now I have my  answer. BUT I HAVE msg == null
    MessageBox.Show(msg.Text);
}

public class WhoWait
{
    public int RefNumMsg;
    public AutoResetEvent WaitForAnswer;
    public Action<MyMessage> MessageSetter;
}

void RegisterToWait(int msgRefNumber, AutoResetEvent waitForAnswer, Action<MyMessage> msgSetter)
{
  WhoWait w = new WhoWait();
  w.RefNumMsg = msgRefNumber;
  w.WaitForAnswer = waitForAnswer;
  w.MessageSetter = msgSetter;

  Waiting.Add(msgRefNumber, w);
}


void OnRecieve()
{
     // Get data from server
     MyMessage msg = GetMessageData();

     // Search for someone waiting for this answer
     WhoWait w = Wainting[msg.RefNum];

     // Set the response message
     w.MessageSetter(msg);

     // Warn to the main thread. Message with their answer arrive
     w.WaitForAnswer.Set();
}
OJ
Thanks, but i don't undertand this " receivedMessage => msg = receivedMessage". And with this not compile.
mgagna
It's a lambda expression that evaluates to an Action<MyMessage>. Which version of C#/.NET/VisualStudio are you using? What compiler error did you get?
OJ
I'm using VS2005. C# 2.0.
mgagna
That would be why.
OJ
+1  A: 

I solve this adding a class who keep the object (the OneMessageIn class).

// This class keep the copy of the message when arrived
public class OneMessageIn
{
    public MyMessage Message
}

void SendAndWait() 
{ 
    AutoResetEvent waitForAnswer = new AutoResetEvent(false); 
    int msgReferenceNumber = 1; 
    OneMenssageIn msgBox = new OneMessageIn(); 

    // Register to wait for answer to the message with the number = msgReferenceNumber 
    RegisterToWait(msgNumber, waitForAnswer, ref msgBox); 
    // Send the request to the server, with the reference number. 
    SendRequest(msgReferenceNumber); 

    // Wait until the server send my answer 
    waitForAnswer.WaitOne(); 

    // Now I have my answer!!!
    MessageBox.Show(msgBox.Message.Text); 
}

public class WhoWait     
{     
    public int RefNumMsg;     
    public AutoResetEvent WaitForAnswer;     
    public OneMessageIn MessageWithAnswer;     
}     
// In this Dicctionary I save who wait for answers.     
private Dictionary<int, WhoWait> Waiting = new Dictionary<WhoWait>();     

// I'm not sure if ref is necesary, but with it works. I don't try without ref.    
void RegisterToWait(int msgRefNumber, AutoResetEvent waitForAnswer, ref OneMessageIn msgBox)    
{    
  WhoWait w = new WhoWait();    
  w.RefNumMsg = msgRefNumber;    
  w.WaitForAnswer = waitForAnswer;    
  w.MessageWithAnswer = msgBox;    

  Waiting.Add(msgRefNumber, w);    
}

void OnRecieve()       
{       
     // Get data from server       
     MyMessage msg = GetMessageData();       

     // Search for someone waiting for this answer       
     WhoWait w = Wainting[msg.RefNum];       

     // Set the response message       
     w.MessageWithAnswer.Message = msg;       

     // Warn to the main thread. Message with their answer arrive       
     w.WaitForAnswer.Set();       
} 

This solution works great for me.

Thanks all who help me with this problem.

mgagna
<quote>All problems in computer science can be solved by another level of indirection.</quote> (according to Butler Lampson and David Wheeler)
Ben Voigt
Amen. Thanks for the quote.
mgagna