Some APIs, like the WebClient, use the Event-based Async pattern. While this looks simple, and probably works well in a loosely coupled app (say, BackgroundWorker in a UI), it doesn't chain together very well.
For instance, here's a program that's multithreaded so the async work doesn't block. (Imagine this is going in a server app and called hundreds of times -- you don't want to block your ThreadPool threads.) We get 3 local variables ("state"), then make 2 async calls, with the result of the first feeding into the second request (so they can't go parallel). State could mutate too (easy to add).
Using WebClient, things end up like the following (or you end up creating a bunch of objects to act like closures):
using System;
using System.Net;
class Program
{
static void onEx(Exception ex) {
Console.WriteLine(ex.ToString());
}
static void Main() {
var url1 = new Uri(Console.ReadLine());
var url2 = new Uri(Console.ReadLine());
var someData = Console.ReadLine();
var webThingy = new WebClient();
DownloadDataCompletedEventHandler first = null;
webThingy.DownloadDataCompleted += first = (o, res1) => {
if (res1.Error != null) {
onEx(res1.Error);
return;
}
webThingy.DownloadDataCompleted -= first;
webThingy.DownloadDataCompleted += (o2, res2) => {
if (res2.Error != null) {
onEx(res2.Error);
return;
}
try {
Console.WriteLine(someData + res2.Result);
} catch (Exception ex) { onEx(ex); }
};
try {
webThingy.DownloadDataAsync(new Uri(url2.ToString() + "?data=" + res1.Result));
} catch (Exception ex) { onEx(ex); }
};
try {
webThingy.DownloadDataAsync(url1);
} catch (Exception ex) { onEx(ex); }
Console.WriteLine("Keeping process alive");
Console.ReadLine();
}
}
Is there an generic way to refactor this event-based async pattern? (I.e. not have to write detailed extension methods for each API thats like this?) BeginXXX and EndXXX make it easy, but this event way doesn't seem to offer any way.
In the past I've implemented this using an iterator method: every time you want another URL requested, you use "yield return" to pass control back to the main program. Once the request finishes, the main program calls back into your iterator to execute the next piece of work.
You're effectively using the C# compiler to write a state machine for you. The advantage is that you can write normal-looking C# code in the iterator method to drive the whole thing.
using System;
using System.Collections.Generic;
using System.Net;
class Program
{
static void onEx(Exception ex) {
Console.WriteLine(ex.ToString());
}
static IEnumerable<Uri> Downloader(Func<DownloadDataCompletedEventArgs> getLastResult) {
Uri url1 = new Uri(Console.ReadLine());
Uri url2 = new Uri(Console.ReadLine());
string someData = Console.ReadLine();
yield return url1;
DownloadDataCompletedEventArgs res1 = getLastResult();
yield return new Uri(url2.ToString() + "?data=" + res1.Result);
DownloadDataCompletedEventArgs res2 = getLastResult();
Console.WriteLine(someData + res2.Result);
}
static void StartNextRequest(WebClient webThingy, IEnumerator<Uri> enumerator) {
if (enumerator.MoveNext()) {
Uri uri = enumerator.Current;
try {
Console.WriteLine("Requesting {0}", uri);
webThingy.DownloadDataAsync(uri);
} catch (Exception ex) { onEx(ex); }
}
else
Console.WriteLine("Finished");
}
static void Main() {
DownloadDataCompletedEventArgs lastResult = null;
Func<DownloadDataCompletedEventArgs> getLastResult = delegate { return lastResult; };
IEnumerable<Uri> enumerable = Downloader(getLastResult);
using (IEnumerator<Uri> enumerator = enumerable.GetEnumerator())
{
WebClient webThingy = new WebClient();
webThingy.DownloadDataCompleted += delegate(object sender, DownloadDataCompletedEventArgs e) {
if (e.Error == null) {
lastResult = e;
StartNextRequest(webThingy, enumerator);
}
else
onEx(e.Error);
};
StartNextRequest(webThingy, enumerator);
}
Console.WriteLine("Keeping process alive");
Console.ReadLine();
}
}
You might want to look into F#. F# can automate this coding for you with its «workflow» feature. The '08 PDC presentation of F# dealt with asynchronous web requests using a standard library workflow called async, which handles the BeginXXX/EndXXX pattern, but you can write a workflow for the event pattern without much difficulty, or find a canned one. And F# works well with C#.
Related
I am trying to find a solution that allows me to write one method in my forms project that can variably call multiple different methods from my class library project.
The reason for this being that I want to implement retry logic around these methods and prevent myself from repeating it for each different variety of method. The only consistent thing about the class library methods are that they all return Task<bool> so its easy to await on and perform logic with.
So far I have the following:
public async Task Runner(string methodName, params object[] parameters)
{
ThreadTimer.Start();
var tries = 0;
var ok = false;
while (tries <= 180)
{
try
{
var parameterTypes = (from p in parameters select p.GetType()).ToArray();
var mi = typeof(string).GetMethod(methodName, parameterTypes); //Currently returns null
var result = (Task<bool>)mi.Invoke(null, parameters);
ok = await result;
if (ok) break;
}
catch (Exception ex)
{
if (ex.InnerException == null)
{
ExceptionLabel2.Text = ex.Message;
}
else
{
ExceptionLabel1.Text = ex.Message;
ExceptionLabel2.Text = ex.InnerException.Message;
}
}
finally
{
tries++;
}
}
if (ok)
{
ThreadTimer.Dispose();
}
else
{
CShellControls.ExitWindowsEx(0, 0); //Logoff
}
}
The idea behind this is to declare a method name in a string and pass an array of parameters with it. I then used .GetMethod() to try and fetch the desired method info but unfortunately this returns null.
I have tried a few different methods but I'm open to suggestions and critique. As far as optimizing code goes I haven't really thought much into it, I just want to try and get this working first before approaching a more efficient method.
Thank you in advance!
I`m using SQLite with multi-threading,my program is working fine but I wish to make it faster. I read that SQLite has 3 threads modes
that can be set compile time(https://www.sqlite.org/threadsafe.html) where the default is "Serialized" but from what I read, the "Multi-thread" would be faster for me.
What I don't understand is how to set SQLite to "Multi-thread" mode in Visual Studio 2013.Anyone know how can I do this? I already found questions talking about this subject but none of them showed clearly how to set this mode.
This is how to make sqlite work in multiple threads.
Use BlockingCollection with ThreadPool.QueueUserWorkItem.
Any database query are queued and executed in FIFO (First In First Out) order.
Now the database is never locked while doing any SQL transaction from any thread.
This is an example in C#.
public class DatabaseQueueBus
{
private BlockingCollection<TransportBean> _dbQueueBus = new BlockingCollection<TransportBean>(new ConcurrentQueue<TransportBean>());
private CancellationTokenSource __dbQueueBusCancelToken;
public CancellationTokenSource _dbQueueBusCancelToken { get => __dbQueueBusCancelToken; set => __dbQueueBusCancelToken = value; }
public DatabaseQueueBus()
{
_dbQueueBusCancelToken = new CancellationTokenSource();
DatabaseQueue();
}
public void AddJob(TransportBean dto)
{
_dbQueueBus.Add(dto);
}
private void DatabaseQueue()
{
ThreadPool.QueueUserWorkItem((param) =>
{
try
{
do
{
string job = "";
TransportBean dto = _dbQueueBus.Take(_dbQueueBusCancelToken.Token);
try
{
job = (string)dto.DictionaryTransBean["job"];
switch (job)
{
case "SaveClasse":
//Save to table here
break;
case "SaveRegistrant":
//Save Registrant here
break;
}
}
catch (Exception ex)
{//TODO: Handle this exception or not
}
} while (_dbQueueBusCancelToken.Token.IsCancellationRequested != true);
}
catch (OperationCanceledException)
{
}
catch (Exception ex)
{
}
});
}
}
Function that throws the ThirdPartyException (I don't know how does their code work) exception:
private void RequestDocuments(/* arguments... */) {
while(true) {
var revision = lastRevision;
var fetchedDocuments = 0;
try {
foreach(var document in connection.QueryDocuments(revision)) {
if(fetchedDocuments > fetchQuota) return;
container.Add(document);
++fetchedDocuments;
Logger.Log.InfoFormat("added document (revision: {0}) into inner container", document.Revision);
}
Logger.Log.Info("Done importing documents into the inner container");
return;
}
catch(Exception ex) {
if(ex is ThirdPartyException) {
// handle this in a certain way!
continue;
}
}
}
}
this function is called inside a worker thread like this:
private void ImportDocuments() {
while(!this.finishedEvent.WaitOne(0, false)) {
try {
var documents = new List<GohubDocument>();
RequestDocuments(remoteServerConnection, documents, lastRevision, 100);
}
catch(Exception ex) {
// here is where it really gets handled!!!?
}
}
}
the exception is handled only in the outermost (which is inside the ImportDocuments method) try/catch.
Why is that?
If that's a LINQ API which exposes IQueryable you don't get an error due to the deferred execution that LINQ to SQL implementations typically uses.
To prevent it you have to invoke .ToList(), FirstOrDefault() etc within your first method. That makes sure that the query really have been executed against your data source.
Solution:
var documents = connection.QueryDocuments(revision).ToList();
foreach(var document in documents) {
if(fetchedDocuments > fetchQuota) return;
// [...]
}
So C#/.NET question. Do I always need to call .EndInvoke when invoking asynchronously with .BeginInvoke some method? I have read somewhere it is mandatory, but the problem is .EndInvoke will block execution? Is there some kind of universal solution?
Yes, you do have to call EndInvoke().
but the problem is .EndInvoke will block execution?
Not when you call it from the callback method, which is the proper patttern.
Yes, it is mandatory for 2 reasons:
1) to avoid possible resoure leak
2) to catch any Exception that might have been thrown
Below is example code:
public delegate void DelegateHelper2(..parameters...);
private void ff(..parameters...);{}
DelegateHelper2 myDelegate = new DelegateHelper2(ff);
// invoke asynchronyously
IAsyncResult result = myDelegate.BeginInvoke(..parameters..., CallBackEmpty, null);
....
private void CallBackEmpty(IAsyncResult iasync)
{
if (iasync != null)
{
string typeName = "";
try
{
System.Runtime.Remoting.Messaging.AsyncResult aresult =
(System.Runtime.Remoting.Messaging.AsyncResult)iasync;
object action1 = aresult.AsyncDelegate;
Type actionType = action1.GetType();
typeName = actionType.ToString();
if (action1 != null)
{
//action1.EndInvoke(iasync);
actionType.InvokeMember("EndInvoke",
System.Reflection.BindingFlags.InvokeMethod, null,
action1, new object[] { iasync });
}
}
catch (Exception ex)
{
string msg = "CallBackEmpty; for type: " + typeName +
" ;Exception: " + ex.ToString();
Setup_TraceExceptions(msg);
}
}
}
So, it seems people are confused with tricks applied above. I will explain. Here is your regular solution, when using .BeginInvoke to spawn a thread and forget it:
private void ff(..parameters...);{}
DelegateHelper2 myDelegate = new DelegateHelper2(ff);
// invoke asynchronyously
IAsyncResult result = myDelegate.BeginInvoke(..parameters..., CallBack2, myDelegate);
private void CallBack2(IAsyncResult iasync)
{
if (iasync != null)
{
try
{
DelegateHelper2 action1 = (DelegateHelper2)iasync.AsyncState;
action1.EndInvoke(iasync);
}
catch (Exception ex)
{
//Trace exeption somehow
}
}
}
There are problems:
1) You need to pass separtely callBack delgate and delgate itself
2) If you are using this pattern offten, (spawning threads in this way), you need for each .BeginInvoke to write a new CallBack method, becous inside it you refer to specific delegate type(DelegateHelper2). ( I in my code have maybe 8 such calls, that would result in 8 different CallBack methods etc. )
Above example does trick and solves those problems, in a this way:
1) You pass only CallBack delegate, action1 is obtained from IAsyncResult
2) Only one CallBack method is needed, since it does not depend on delegate type (that is solved by reflection)
i have a problem with Winforms app with 2 threads: in a second thread I pull messages from WCF service. When there are messages I need to update the GUI : I do this in accordance with patten found here How to update the GUI from another thread in C#? . Here is the code:
private delegate void CWU(int ID);
public void AddNewTab(int id)
{
if (this.tabControl1.InvokeRequired)
{
CWU cb = new CWU(AddNewTab);
this.tabControl1.Invoke(cb,id);
}
else
{
User ToChatWith = ContactsHelper.AllFriends.Find(e => e.ID == id);
tabpage.Text = ToChatWith.ToString();
this.tabControl1.TabPages.Add(tabpage);
this.tabControl1.SelectTab(tabpage);
}
tab is added properly and when we leave this method the app is not responding, no info in debug. When I run my app after adding this tab i get AppHangB1 without any details. Can you help me?
Try this:
if (this.InvokeRequired)
{
this.Invoke((MethodInvoker)delegate
{
AddNewTab(id);
});
}
Invoke can hang if the called thread is busy doing something. (You could know this is the case if a BeginInvoke call instead of your Invoke wouldn't hang. Invoke blocks until the call is made successfully, BeginInvoke does not.)
Replace your method with this:
private void dbg(string s)
{
System.Diagnostics.Debug.WriteLine("AddNewTab({0}): {1}",
Thread.CurrentThread.ManagedThreadId, s);
}
public void AddNewTab(int id)
{
try
{
dbg("entered");
if (this.tabControl1.InvokeRequired)
{
new Thread(delegate() { try {
CWU cb = new CWU(AddNewTab);
dbg("calling Invoke");
this.tabControl1.Invoke(cb, id);
dbg("Invoke returned");
} catch (Exception ex) { dbg("" + ex); }
}).Start();
dbg("created sub-thread");
}
else
{
dbg("setting tabpage.Text");
User ToChatWith = ContactsHelper.AllFriends
.Find(e => e.ID == id);
tabpage.Text = ToChatWith.ToString();
dbg("adding tab");
this.tabControl1.TabPages.Add(tabpage);
this.tabControl1.SelectTab(tabpage);
dbg("done adding tab");
}
dbg("leaving");
}
catch (Exception ex)
{
dbg("" + ex);
}
}
Make sure you can find the debugger output in your environment. (Heck, use Console.WriteLine if that helps)
If that doesn't help you diagnose the problem I don't know what will.