I've got a cross thread invoke going on, and the invoke works fine, however I have found that all subsequent function calls need to be invoked aswell, otherwise it throws exceptions, how do I go about either modifiying my invoke command, or through some other solution, fix this?
This is my current invoker:
foreach (chat_window oc in open_chats)
{
if (oc.Name == friend)
{
string updmsg = msg.Substring(msg.IndexOf('\n')).Trim();
string[] argNames = new string[2];
argNames[0] = friend;
argNames[1] = updmsg;
Type t = oc.GetType();
t.InvokeMember("updateChat",
System.Reflection.BindingFlags.InvokeMethod, null, oc, argNames);
}
}
Thanks in advance, Psy
I assmue that chat_window is some WinForms object derived from Control?
If so then you probably want to use Control.Invoke/Control.BeginInvoke in order to marshall the call back to the UI thread.
oc.Invoke(oc.updateChat, argNames);
Related
I have a method that is ran in the main application thread, but creates new Task for long-running operation and awaits it's result.
public async Task<CalculatorOutputModel> CalculateXml(CalculatorInputModel input)
{
// 1
return await Task.Factory.StartNew(
new Func<CalculatorOutputModel> (() =>
{
// 2
using (var ms = Serializer.Serialize(input))
{
ms.Position = 0;
using (var rawResult = Channel.RawGetFrbXmlOutputs(ms)) // 3
{
var result = Parser.Parse(rawResult);
return result;
}
}
}),
CancellationToken.None,
TaskCreationOptions.None,
TaskScheduler.FromCurrentSynchronizationContext());
}
My problem is that the AppContext in points (1) and (2) are "a bit" different. In point 1 there is usual application context in Current property, that has all the data I need. In point 2, as you understand, there is null reference in AppContext.Current, so I can't access any data. The problem with accessing context in point 2 seems to be easy, just "catch" current context in local variable or pass it as the parameter. The problem for me is more difficult because I need to access the context somewhere in the deep of line marked as "3".
The class itself is derived from System.ServiceModel.ClientBase<TChannel> and the place where I need to get access to the context is the class that implements IClientMessageInspector.
class CalculatorMessageInspector : IClientMessageInspector
{
public object BeforeSendRequest(ref Message request, IClientChannel channel)
{
if (AppContext.Current != null)
{
// never goes here
}
}
}
Just to clarify, here is the call stack (I can't pass my context inside required method):
So:
I can't pass context to Channel's method because it does not make any sense;
I can't save required parameters from context in Channel because it is proxy class;
I can't save required parameters in CalculatorMessageInspector because it is created within the place where current context is already null.
Can anyone advise any method how can I stay within the same context in another thread? Or, at least, how can I pass parameter from the place marked "2" inside the methods hierarchy? Maybe, I can use SynchronizationContext somehow to achieve it? Thanks a lot for any suggestions.
Update
AppContext.Current consider to be the same as HttpContext.Current != null ? HttpContext.Current.Items[AppContextKey] : null
Update 2
So it seems, that in this case there is no common solution to persist the context. Hence, the only applicable solution in this concrete situation (that is quite specific) is to capture required parameters using closure and save then in an object, that will be available in required method (for me worked adding properties to the implementator of IEndpointBehavior, but that solution is a bit odd). Thereby, the most applicable solution is to throw away the asynchronous wrapping over the sync call, so the AppContext never "goes away". Mark Stephen's answer as the right then.
To use async and await on ASP.NET, you must target .NET 4.5 and turn off quirks mode. The best way to do this is to set /configuration/system.web/httpRuntime#targetFramework="4.5" in your web.config.
Also, you shouldn't use Task.Run or Task.Factory.StartNew on ASP.NET. So your code should look more like this:
public CalculatorOutputModel CalculateXml(CalculatorInputModel input)
{
using (var ms = Serializer.Serialize(input))
{
ms.Position = 0;
using (var rawResult = Channel.RawGetFrbXmlOutputs(ms))
{
var result = Parser.Parse(rawResult);
return result;
}
}
}
I hope I can word this correctly. I have a WCF Service that I'm using (duplex channel communications) in which one client registers with the service. The service's registration method returns a value. I want the the method of the called service registration method to also call the callback method that will send out notification of the client registration (I have my reasons for this and explaining it here will only confuse the issue). The problem is that the client's implemented callback has to run in the main application thread to work correctly (due mostly to integration with a third-party application). The service registration method call is also occuring in this same thread, so it effectively locks up since the client is looking for a return from the service registration method holding on to the thread preventing the callback method from being able to run. If I tell it to call all callback methods for all contexts other than the one just registered, it works just fine. But if I tell it to include it, obviously it locks up because that thread is already locked up. I can set the callback attribute property for UseSynchronizationContext to false, but then this means the callback method is called on a separate thread from the main and now the rest of the program will not work. Any help would be greatly appreciated.
Here's basically that registration method (first draft..)
[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerSession,
UseSynchronizationContext = false,
ConcurrencyMode = ConcurrencyMode.Multiple,
Namespace = "http://MyApp/Design/CADServiceTypeLibrary/2012/12")]
public class DTOTransactionService : IDTOTransactionService, IDisposable
{
//some more stuff
public CADManager RegisterCADManager(int processID, bool subscribeToMessages)
{
List<CADManager> cadMgrs = this.CADManagers;
bool registered = false;
//Create new CADManager mapped to process id
CADManager regCADManager = new CADManager(processID);
//Add to CADManagers List and subscribe to messages
if (regCADManager.IsInitialized)
{
cadMgrs.Add(regCADManager);
this.CADManagers = cadMgrs;
//Subscribe to callbacks
if (subscribeToMessages)
SubscribeCallBack(regCADManager.ID);
registered = true;
}
//Send registration change notification
RegistrationState state;
if (registered)
state = RegistrationState.Registered;
else
state = RegistrationState.RegistrationException;
foreach (CallBackSubscriber subscriber in this.CallBackSubscribers)
{
subscriber.CallBackProxy.CADManagerRegistrationNotification(regCADManager.ID, state);
}
return regCADManager;
}
}
I think I've got it figured out. It struck me that a little deeper what's happening is that since the call to the service method is expecting a return value and since the callback will occur in the same thread as the client method expecting a return value that this could be the result of the deadlock condition. I then decided to try calling the callback methods in the service using a different thread to work around the current thread condition. In other words, work around the current thread whose method has yet to have provided a return value from the service method. It worked! Was this the right approach? I have enough experience to be dangerous here, so if someone else's experience shows this to be the wrong way to handle this, I'm all ears.
Thread notifyThread = new Thread(delegate()
{
foreach (CallBackSubscriber subscriber in this.CallBackSubscribers)
{
subscriber.CallBackProxy.CADManagerRegistrationNotification(regCADManager.ID, state);
}
});
Update:
Yes, the threading and deadlock condition was the issue, however the more appropriate fix I recently discovered is to use SynchronizationContext. To use, create a property or field of the type SynchronizationContext, then assign the value to the field/property while in the context you wish to capture using SynchronizationContext.Current. Then, use the Post() method (providing it a delegate via the SendOrPostCallback object) in the callback method being called by the Service. A short example:
private SynchronizationContext _appSyncContext = null;
private DTOCommunicationsService()
{
this.AppSyncContext = SynchronizationContext.Current;
//Sets up the service proxy, etc, etc
Open();
}
// Callback method
public void ClientSubscriptionNotification(string clientID, SubscriptionState subscriptionState)
{
SendOrPostCallback callback = delegate(object state)
{
object[] inputArgs = (object[])state;
string argClientID = (string)inputArgs[0];
SubscriptionState argSubState = (SubscriptionState)inputArgs[1];
//Do stuff with arguments
};
_appSyncContext.Post(callback, new object[] { clientID, subscriptionState });
}
I have a thread that loads data when a service is starting. I call a method in this thread that uses Parallel.Foreach to iterate a set of data. But the linq query that I have inside the parallel forerach, gets a objet reference not set to an instance error.
*The same logic works if I remove the Parallel.Foreach though or if I remove the thread. *Even locking the list doesnt help.**
loadingThread = new Thread(new ThreadStart(PreloadData));
loadingThread.IsBackground = true;
loadingThread.Start();
---------------------------------------
public static void PreloadData()
{
Parallel.ForEach(loadedIDs.Keys, indexDefId =>
{
List<FixingEvent> lst = null;
lock (loadedEvents)
{
lst = (from e in loadedEvents where e.DIVACode.Equals(indexDefId) select e).ToList();
}
---------------------------
}
I get an exception in the linq query inside - ' e is null and hence object reference error'.
Any help is appreciated.
You should not lock the loadedEvents object. According to msdn:
Best practice is to define a private object to lock on, or a private
static object variable to protect data common to all instances.
you do not need to lock the object you are accessing - you simply need an object to lock on.
I guess the list loadedEvents contains null elements. Maybe due to a race condition.
Edit: I updated my code. Would this achieve what i am aiming for?
I have a working set of methods for async calling of methods but i have a specific problem with the references i pass in via a lambda. Specifically i have a (child) window that starts an operation and registers a callback. As you might expect, even when i close this window it still gets invoked.
What i want to do is pass in a kind of "weak reference" or construct a weak reference out of the incoming action.
Thhis is the way i build my Action (example code):
static Action CreateNewAction(Action call, Action<SomeArg> callback,
Dispatcher dispatcher)
{
return delegate {
try
{
call();
var target = callback.Target
if(target != null)
dispatcher.Invoke(callback, new SomeArg());
}
catch (Exception ex)
{
// handle the ex in some way..
}
};
}
And this is how the task factory calls it:
var t = Task.Factory.StartNew(CreateNewAction(call, callback, dispatcher))
And this is how I would call it (the call just basses both the action and the callback through to the task factory as seen above):
WeakReference wr = new WeakReference(myTarget);
StartMyTaskAsync(someAction, ((MyTargetClass)wr.Target).SomeCompletedFunc);
The problem comes from this line:
StartMyTaskAsync(someAction, ((MyTargetClass)wr.Target).SomeCompletedFunc);
Specifically, this part:
((MyTargetClass)wr.Target).SomeCompletedFunc
You are materializing the target of the WeakReference to get the method that is referenced by the lambda/delegate long before you want to actually check whether or not the WeakReference has let go of the Target.
What you really want to do is pass a wrapper for your delegate, something like this (you didn't show the signature of SomeCompletedFunc so I don't know exactly what the call will be like, I'm assuming it's a parameterless void method for the purposes of this question):
StartMyTaskAsync(someAction, () => {
// Check the weak reference. If null, return.
var target = wr.Target as MyTargetClass;
// If null, return.
if (target == null) return;
// If not null, call.
target.SomeCompletedFunc();
});
This way, you check the Target of the WeakReference at the time you want to make the method call, and not when you assign the callback.
This question already has answers here:
How do I update the GUI from another thread?
(47 answers)
Closed 5 years ago.
I'm new with C# and I'm trying to make a simple client server chat application.
I have RichTextBox on my client windows form and I am trying to update that control from server which is in another class. When I try to do it I get the error: "Cross-thread operation not valid: Control textBox1 accessed from a thread other than the thread it was created on".
Here the code of my Windows form:
private Topic topic;
public RichTextBox textbox1;
bool check = topic.addUser(textBoxNickname.Text, ref textbox1, ref listitems);
Topic class:
public class Topic : MarshalByRefObject
{
//Some code
public bool addUser(string user, ref RichTextBox textBox1, ref List<string> listBox1)
{
//here i am trying to update that control and where i get that exception
textBox1.Text += "Connected to server... \n";
}
So how to do that? How can I update the textbox control from another thread?
I'm trying to make some basic chat client/server application using .net remoting.
I want to make windows form client application and console server application as separate .exe files. Here im trying to call server function AddUser from client and i want to AddUser function update my GUI. Ive modified code as you suggested Jon but now instead of cross-thread exception i've got this exception ... "SerializationException: Type Topic in Assembly is not marked as serializable".
Ill post my whole code bellow, will try to keep it simple as possible.
Any suggestion is welcome. Many thanks.
Server:
namespace Test
{
[Serializable]
public class Topic : MarshalByRefObject
{
public bool AddUser(string user, RichTextBox textBox1, List<string> listBox1)
{
//Send to message only to the client connected
MethodInvoker action = delegate { textBox1.Text += "Connected to server... \n"; };
textBox1.BeginInvoke(action);
//...
return true;
}
public class TheServer
{
public static void Main()
{
int listeningChannel = 1099;
BinaryServerFormatterSinkProvider srvFormatter = new BinaryServerFormatterSinkProvider();
srvFormatter.TypeFilterLevel = TypeFilterLevel.Full;
BinaryClientFormatterSinkProvider clntFormatter = new BinaryClientFormatterSinkProvider();
IDictionary props = new Hashtable();
props["port"] = listeningChannel;
HttpChannel channel = new HttpChannel(props, clntFormatter, srvFormatter);
// Register the channel with the runtime
ChannelServices.RegisterChannel(channel, false);
// Expose the Calculator Object from this Server
RemotingConfiguration.RegisterWellKnownServiceType(typeof(Topic),
"Topic.soap",
WellKnownObjectMode.Singleton);
// Keep the Server running until the user presses enter
Console.WriteLine("The Topic Server is up and running on port {0}", listeningChannel);
Console.WriteLine("Press enter to stop the server...");
Console.ReadLine();
}
}
}
}
Windows form client:
// Create and register a channel to communicate to the server
// The Client will use the port passed in as args to listen for callbacks
BinaryServerFormatterSinkProvider srvFormatter = new BinaryServerFormatterSinkProvider();
srvFormatter.TypeFilterLevel = TypeFilterLevel.Full;
BinaryClientFormatterSinkProvider clntFormatter = new BinaryClientFormatterSinkProvider();
IDictionary props = new Hashtable();
props["port"] = 0;
channel = new HttpChannel(props, clntFormatter, srvFormatter);
//channel = new HttpChannel(listeningChannel);
ChannelServices.RegisterChannel(channel, false);
// Create an instance on the remote server and call a method remotely
topic = (Topic)Activator.GetObject(typeof(Topic), // type to create
"http://localhost:1099/Topic.soap" // URI
);
private Topic topic;
public RichTextBox textbox1;
bool check = topic.addUser(textBoxNickname.Text,textBox1, listitems);
You need to either use BackgroundWorker, or Control.Invoke/BeginInvoke. Anonymous functions - either anonymous methods (C# 2.0) or lambda expressions (C# 3.0) make this easier than it was before.
In your case, you can change your code to:
public bool AddUser(string user, RichTextBox textBox1, List listBox1)
{
MethodInvoker action = delegate
{ textBox1.Text += "Connected to server... \n"; };
textBox1.BeginInvoke(action);
}
A few things to note:
To conform with .NET conventions, this should be called AddUser
You don't need to pass the textbox or listbox by reference. I suspect you don't quite understand what ref really means - see my article on parameter passing for more details.
The difference between Invoke and BeginInvoke is that BeginInvoke won't wait for the delegate to be called on the UI thread before it continues - so AddUser may return before the textbox has actually been updated. If you don't want that asynchronous behaviour, use Invoke.
In many samples (including some of mine!) you'll find people using Control.InvokeRequired to see whether they need to call Invoke/BeginInvoke. This is actually overkill in most cases - there's no real harm in calling Invoke/BeginInvoke even if you don't need to, and often the handler will only ever be called from a non-UI thread anyway. Omitting the check makes the code simpler.
You can also use BackgroundWorker as I mentioned before; this is particularly suited to progress bars etc, but in this case it's probably just as easy to keep your current model.
For more information on this and other threading topics, see my threading tutorial or Joe Albahari's one.
Use Invoke method
// Updates the textbox text.
private void UpdateText(string text)
{
// Set the textbox text.
yourTextBox.Text = text;
}
Now, create a delegate that has the same signature as the method that was previously defined:
public delegate void UpdateTextCallback(string text);
In your thread, you can call the Invoke method on yourTextBox, passing the delegate to call, as well as the parameters.
yourTextBox.Invoke(new UpdateTextCallback(this.UpdateText),
new object[]{”Text generated on non-UI thread.”});