Why does JsonConvert.SerializeObject fail when called from a nested task? - c#

I have a console program which starts a long running task, which in turn starts 4 x long running tasks. By long running I mean all these tasks are supposed to run indefinitely.
However, calling JsonConvert.Serialize from any of the nested tasks crashes the app silently, without any error messages.
I've recreated this behaviour with a simple console program:
public class TestClass
{
public string ATestProperty { get; set; }
}
internal class Program
{
static void Main(string[] args)
{
var outerTask = Task.Factory.StartNew(() =>
{
var nestedTask = Task.Factory.StartNew(() =>
{
Console.WriteLine("Inside nestedTask");
var testClass = new TestClass { ATestProperty = "Hi from test class" };
var serialisedClass = JsonConvert.SerializeObject(testClass);
Console.WriteLine(serialisedClass);
});
});
Console.WriteLine("DONE");
}
}
This prints
DONE
Inside nestedTask
If I debug and step through the code, the program terminates right after executing
var serialisedClass = JsonConvert.SerializeObject(testClass);
I'm not sure what I'm missing, perhaps it is a combination of a task + serialisation behaviour that I'm not aware of. I've tried System.Text.Json to serialise my data too with the same results.

You can add Task.WaitAll(Task task) after each block of nested task.
Example:
var outerTask = Task.Factory.StartNew(() =>
{
var nestedTask = Task.Factory.StartNew(() =>
{
Console.WriteLine("Inside nestedTask");
var testClass = new TestClass { ATestProperty = "Hi from test class" };
var serialisedClass = JsonConvert.SerializeObject(testClass);
Console.WriteLine(serialisedClass);
});
Task.WaitAll(nestedTask);
});
Task.WaitAll(outerTask);
Console.WriteLine("DONE");

Related

Load and concurrency tests for WCF service

Since this morning, I have to load/stress test a WCF service and concurrency access (within an app console to be simple), but the WCF service is totally opaque to me. I can't edit it or view the source code.
What I've tried so far :
So, in the Main() program, I am calling a method to initialize some services :
private static void Init()
{
_serviceCommunicationMock = Mock.Of<ITrackingCommunicationService>();
Mock.Get(_serviceCommunicationMock).Setup(x => x.GetIdentityCardAsync(It.IsAny<string>())).ReturnsAsync(() =>
{
new TrackingWcfCommunicationOptions
{
EndPointAddress = "http://172.16.0.2/IIT/[COMPANY].Tracking/TrackingService"
};
string jsonContent = System.IO.File.ReadAllText(GetRandomFile());
return jsonContent.Parse();
});
_serviceValidationMock = Mock.Of<IIdentityCardValidation>();
Mock.Get(_serviceValidationMock).Setup(x => x.VerifyProductIdentityCard(It.IsAny<IdentityCard>())).Returns(
new [COMPANY].Tracking.Core.Business.Model.ResultValidation()
{
IsValid = true,
ErrorMessages = new List<string>()
});
_trackingManager = new TrackingManager(_serviceCommunicationMock, _serviceValidationMock);
}
After this, I'm calling an other method to try to stress tests my WCF as following :
private async static void DoSomething()
{
var taskList = new List<Task<IdentityCard>>();
for (int iterator = 0; iterator < 1; iterator++)
{
async Task<IdentityCard> func()
{
var response = await _trackingManager.GetIdentityCardAsync("Vhdn3UpJsDp6ue6LQWy5gZ");
return response;
}
taskList.Add(func());
}
await Task.WhenAll(taskList);
foreach (var task in taskList)
{
Console.WriteLine(task.Result);
}
}
But the problem is in the Init() method, it already returns a random file but I excepted to call the _trackingManager.GetIdentityCardAsync([GUID]). But it simple impossible as I'm a beginner in Moq AND in WCF.
Could someone help me ?

Async seems to be synchronous

I am not sure if I am missing something here but more for loop seems to be executing synchronously even though I await all tasks out side of it.
Here is my code below:
static void Main(string[] args) {
var t = Start();
}
public static async Task < List < Task < TaskInfo >>> Start() {
var listOfTasks = new List < Task < TaskInfo >> ();
for (var i = 0; i <= 100; i++) {
var process = new Processor();
listOfTasks.Add(process.Process(i));
}
await Task.WhenAll(listOfTasks);
return listOfTasks;
}
I pass in the taskId to log out just to see the order the tasks execute.
Am I missing something really obvious here?
EDIT:
Changed code to this based on the answers and comments below and it still appears synchronously:
public class StartWork
{
public int TaskId { get; set; }
public Processor Processor { get;}
public StartWork()
{
Processor = new Processor();
}
}
static void Main(string[] args)
{
var t = Start();
}
public static async Task<TaskInfo[]> Start()
{
var tasks = new List<StartWork>();
for (int i = 1; i < 100; i++)
{
var work = new StartWork
{
TaskId = i
};
tasks.Add(work);
}
return await Task.WhenAll(tasks.Select(i => i.Processor.Process(i.TaskId)));
}
The function I am calling in processor class:
public Task<TaskInfo> Process(int taskId)
{
try
{
taskId = taskId + 1;
stopwatch.Start();
using (var bus = RabbitHutch.CreateBus(xxDev))
{
#event = new AutoResetEvent(false);
var replyTo = Guid.NewGuid().ToString();
var messageQueue = bus.Advanced.QueueDeclare(replyTo, autoDelete: true);
bus.Advanced.Consume(messageQueue, (payload, properties, info) =>
{
ReceivePdf(payload, properties, info);
return Task.FromResult(0);
});
taskInfo.InputFile = inputFile;
var html = File.ReadAllText(inputFile);
taskInfo.Html = html;
var message = PrepareMessage(new RenderRequest()
{
Html = Encoding.UTF8.GetBytes(html),
Options = new RenderRequestOptions()
{
PageSize = "A4",
ImageQuality = 70,
PageLoadRetryAttempts = 3
}
});
var correlation = Guid.NewGuid().ToString();
Console.WriteLine($"CorrelationId: {correlation}, TaskId {taskId}");
var props = new MessageProperties
{
CorrelationId = correlation,
ReplyTo = replyTo,
Expiration = "6000"
};
Publish(bus, props, message);
taskInfo.CorrelationId = Guid.Parse(correlation);
#event.WaitOne();
stopwatch.Stop();
taskInfo.TimeTaken = stopwatch.Elapsed;
return Task.FromResult(taskInfo);
}
}
catch (Exception e)
{
taskInfo.OutputFile = Empty;
return Task.FromResult(taskInfo);
}
}
void ReceivePdf(byte[] payload, MessageProperties properties, MessageReceivedInfo info)
{
var file = Format(outputFile, properties.CorrelationId);
taskInfo.OutputFile = file;
Console.WriteLine("Output written to " + file);
File.WriteAllBytes(file, payload);
var remaining = Interlocked.Decrement(ref outstandingRequests);
if (remaining == 0)
{
#event.Set();
}
}
This is a synchronous task
listOfTasks.Add(process.Process(i));
You are just adding items to the list.
This is also a synchronous task
process.Process(i);
The task that the function above returns is asynchronous and it will execute asynchronously in the whenAll call of your code.
Bear in mind that when all will wait for all tasks to run and if the task is trivial, since they start one after the other, will most times run sequentially by chance.
You will see some difference if the task code executed differentiated in execution time based on input.
First async doesn't mean multithread, async is used to run background task without blocking UI or to run I/O operations without bloking main thread.
Usually the operating system handles async with multithreading, but there is no guarantee.
If you want be sure to start multiple threads use Thread.Start.
Anyway in your code you force your code to run synchronously, because you call the async method start in the Main method without await.
You need to change the code to:
static async void Main(string[] args)
{
var t = await Start();
}
or without waiting to (but the program risk to terminate before the task complete):
static void Main(string[] args)
{
Task.Run(async () => {
var t = await Start();
});
}

Asynchronous console program hangs on completion of task using CefSharp

In my quest to create the perfect string result = browser.Browse(url) method, I have created a simple class library to demonstrate CefSharp. The code for that is:
public class CefSharpHeadlessBrowser
{
public CefSharpHeadlessBrowser()
{
Cef.Initialize(new CefSettings { CachePath = "cache" }, false, true);
}
public string Browse(string url)
{
Task<string> result;
var browserSettings = new BrowserSettings { WindowlessFrameRate = 1 };
using (var browser = new ChromiumWebBrowser(url, browserSettings))
{
browser.WaitForBrowserToInitialize();
browser.LoadPageAsync();
// Wait awhile for Javascript to finish executing.
Thread.Sleep(2000);
result = browser.GetSourceAsync();
Thread.Sleep(100);
}
return result.Result;
}
}
public static class CefExtensions
{
public static void WaitForBrowserToInitialize(this ChromiumWebBrowser browser)
{
while (!browser.IsBrowserInitialized)
{
Task.Delay(100);
}
}
public static Task LoadPageAsync(this IWebBrowser browser)
{
var tcs = new TaskCompletionSource<bool>();
EventHandler<LoadingStateChangedEventArgs> handler = null;
handler = (sender, args) =>
{
if (!args.IsLoading)
{
browser.LoadingStateChanged -= handler;
tcs.TrySetResult(true);
}
};
browser.LoadingStateChanged += handler;
return tcs.Task;
}
}
This is the test harness, in a separate console project that references the CefSharpHeadlessBrowser project:
class Program
{
static void Main(string[] args)
{
const string searchUrl = "https://www.google.com";
var browser = new CefSharpHeadlessBrowser();
var result = browser.Browse(searchUrl);
Console.Write(result);
}
}
This actually works; it gets the HTML page source properly and displays it in the console window, just as it should. But here's the problem: the console program hangs after displaying the page source. It should exit immediately. Which must mean that I'm doing something wrong with the asynchronous operations and causing a deadlock.
What could be the issue?
CefSharp has a Shutdown command; I was able to solve the problem by adding the following method to the CefSharpHeadlessBrowser class:
public void Shutdown()
{
Cef.Shutdown();
}
And then changing the Test Harness to:
class Program
{
static void Main(string[] args)
{
const string searchUrl = "https://www.google.com";
var browser = new CefSharpHeadlessBrowser();
var result = browser.Browse(searchUrl);
Console.WriteLine(result);
browser.Shutdown(); // Added
}
}
This undoubtedly frees up any remaining threads that are running.
I'll probably make the class IDisposable and wrap the calling code in a using statement.

Cleaning up CallContext in TPL

Depending on whether I'm using async/await based code or TPL based code, I'm getting two different behaviors regarding the clean-up of logical CallContext.
I can set and clear logical CallContext exactly as I expect if I use the following async/await code:
class Program
{
static async Task DoSomething()
{
CallContext.LogicalSetData("hello", "world");
await Task.Run(() =>
Debug.WriteLine(new
{
Place = "Task.Run",
Id = Thread.CurrentThread.ManagedThreadId,
Msg = CallContext.LogicalGetData("hello")
}))
.ContinueWith((t) =>
CallContext.FreeNamedDataSlot("hello")
);
return;
}
static void Main(string[] args)
{
DoSomething().Wait();
Debug.WriteLine(new
{
Place = "Main",
Id = Thread.CurrentThread.ManagedThreadId,
Msg = CallContext.LogicalGetData("hello")
});
}
}
The above outputs the following:
{ Place = Task.Run, Id = 9, Msg = world }
{ Place = Main, Id = 8, Msg = }
Notice the Msg = which indicates that CallContext on the main thread has been freed and is empty.
But when I switch to pure TPL / TAP code I can't achieve the same effect...
class Program
{
static Task DoSomething()
{
CallContext.LogicalSetData("hello", "world");
var result = Task.Run(() =>
Debug.WriteLine(new
{
Place = "Task.Run",
Id = Thread.CurrentThread.ManagedThreadId,
Msg = CallContext.LogicalGetData("hello")
}))
.ContinueWith((t) =>
CallContext.FreeNamedDataSlot("hello")
);
return result;
}
static void Main(string[] args)
{
DoSomething().Wait();
Debug.WriteLine(new
{
Place = "Main",
Id = Thread.CurrentThread.ManagedThreadId,
Msg = CallContext.LogicalGetData("hello")
});
}
}
The above outputs the following:
{ Place = Task.Run, Id = 10, Msg = world }
{ Place = Main, Id = 9, Msg = world }
Is there anything I can do to coerce TPL to "free" the logical CallContext the same way as the async/await code does?
I am not interested in alternatives to CallContext.
I'm hoping to get the above TPL/TAP code fixed so that I can use it in projects targeting the .net 4.0 framework. If that is not possible in .net 4.0, I'm still curious if it can be done in .net 4.5.
In an async method the CallContext is copied on write:
When an async method starts, it notifies its logical call context to activate copy-on-write behavior. This means the current logical call context is not actually changed, but it is marked so that if your code does call CallContext.LogicalSetData, the logical call context data is copied into a new current logical call context before it is changed.
From Implicit Async Context ("AsyncLocal")
That means that in your async version the CallContext.FreeNamedDataSlot("hello") continuation is redundant as even without it:
static async Task DoSomething()
{
CallContext.LogicalSetData("hello", "world");
await Task.Run(() =>
Console.WriteLine(new
{
Place = "Task.Run",
Id = Thread.CurrentThread.ManagedThreadId,
Msg = CallContext.LogicalGetData("hello")
}));
}
The CallContext in Main wouldn't contain the "hello" slot:
{ Place = Task.Run, Id = 3, Msg = world }
{ Place = Main, Id = 1, Msg = }
In the TPL equivalent all code outside the Task.Run (which should be Task.Factory.StartNew as Task.Run was added in .Net 4.5) runs on the same thread with the same exact CallContext. If you want to clean it you need to do that on that context (and not in the continuation):
static Task DoSomething()
{
CallContext.LogicalSetData("hello", "world");
var result = Task.Factory.StartNew(() =>
Debug.WriteLine(new
{
Place = "Task.Run",
Id = Thread.CurrentThread.ManagedThreadId,
Msg = CallContext.LogicalGetData("hello")
}));
CallContext.FreeNamedDataSlot("hello");
return result;
}
You can even abstract a scope out of it to make sure you always clean up after yourself:
static Task DoSomething()
{
using (CallContextScope.Start("hello", "world"))
{
return Task.Factory.StartNew(() =>
Debug.WriteLine(new
{
Place = "Task.Run",
Id = Thread.CurrentThread.ManagedThreadId,
Msg = CallContext.LogicalGetData("hello")
}));
}
}
Using:
public static class CallContextScope
{
public static IDisposable Start(string name, object data)
{
CallContext.LogicalSetData(name, data);
return new Cleaner(name);
}
private class Cleaner : IDisposable
{
private readonly string _name;
private bool _isDisposed;
public Cleaner(string name)
{
_name = name;
}
public void Dispose()
{
if (_isDisposed)
{
return;
}
CallContext.FreeNamedDataSlot(_name);
_isDisposed = true;
}
}
}
A good question. The await version may not work the way you may think it does here. Let's add another logging line inside DoSomething:
class Program
{
static async Task DoSomething()
{
CallContext.LogicalSetData("hello", "world");
await Task.Run(() =>
Debug.WriteLine(new
{
Place = "Task.Run",
Id = Thread.CurrentThread.ManagedThreadId,
Msg = CallContext.LogicalGetData("hello")
}))
.ContinueWith((t) =>
CallContext.FreeNamedDataSlot("hello")
);
Debug.WriteLine(new
{
Place = "after await",
Id = Thread.CurrentThread.ManagedThreadId,
Msg = CallContext.LogicalGetData("hello")
});
}
static void Main(string[] args)
{
DoSomething().Wait();
Debug.WriteLine(new
{
Place = "Main",
Id = Thread.CurrentThread.ManagedThreadId,
Msg = CallContext.LogicalGetData("hello")
});
Console.ReadLine();
}
}
Output:
{ Place = Task.Run, Id = 10, Msg = world }
{ Place = after await, Id = 11, Msg = world }
{ Place = Main, Id = 9, Msg = }
Note the "world" is still there after await, because it was there before await. And it is not there after DoSomething().Wait() because it wasn't there before it, in the first place.
Interestingly enough, the async version of DoSomething creates a copy-on-write clone of the LogicalCallContext for its scope, upon the first LogicalSetData. It does that even when there is no asynchrony inside it - try await Task.FromResult(0). I presume the whole ExecutionContext gets cloned for the scope of the async method, upon the 1st write operation.
OTOH, for the non-async version there is no "logical" scope and no outer ExecutionContext here, so the copy-on-write clone of ExecutionContext becomes current for the Main thread (but the continuations and the Task.Run lambdas still get their own clones). So, you'd either need to move CallContext.LogicalSetData("hello", "world") inside the Task.Run lambda, or clone the context manually:
static Task DoSomething()
{
var ec = ExecutionContext.Capture();
Task task = null;
ExecutionContext.Run(ec, _ =>
{
CallContext.LogicalSetData("hello", "world");
var result = Task.Run(() =>
Debug.WriteLine(new
{
Place = "Task.Run",
Id = Thread.CurrentThread.ManagedThreadId,
Msg = CallContext.LogicalGetData("hello")
}))
.ContinueWith((t) =>
CallContext.FreeNamedDataSlot("hello")
);
task = result;
}, null);
return task;
}

Is the WaitHandle the best option?

I'm working on an MVC application, that uses some Windows Workflow behind the scenes for automation.
I have implemented some code to wait for the Workflow to complete. below is a sample app that boils down the problem to its key parts.
The issue doesn't really have to do with the work going on in the WF activity, but more how I'm waiting for it to complete.
HomeController.cs
public ActionResult Index()
{
return View();
}
[HttpPost]
public JsonResult ProcessRequest()
{
int[] arr = new int[0];
var wh = new ManualResetEvent(false);
var instance = new Activities.SampleCodeActivity();
var args = new Dictionary<string, object>();
args.Add("Limit", 25);
var app = new WorkflowApplication(instance, args);
app.Completed = resultArgs =>
{
var list = (List<int>)resultArgs.Outputs["Primes"];
arr = list.ToArray();
wh.Set();
};
app.Run();
wh.WaitOne();
return Json(arr);
}
Index.cshtml
#{ ViewBag.Title = "Index"; }
<script src="../../Scripts/jquery-1.7.1.min.js"></script>
<script type="text/javascript">
var tools = {};
tools.processRequest = function () {
$.ajax({
url: "#Url.Action("ProcessRequest")", type: "POST",
success: function (data) {
alert(data);
}
});
};
$(document).ready(function () {
tools.processRequest();
});
</script>
<h2>Index</h2>
SampleCodeActivity.cs
public class SampleCodeActivity : CodeActivity
{
public InArgument<int> Limit { get; set; }
public OutArgument<List<int>> Primes { get; set; }
private List<int> _list = new List<int>();
protected override void Execute(CodeActivityContext context)
{
var limit = context.GetValue(Limit);
checkForPrimes(limit);
context.SetValue(Primes, _list);
}
private void checkForPrimes(int limit)
{
for (var x = 2; x <= limit; x++)
if (isPrime(x)) _list.Add(x);
}
private bool isPrime(int value)
{
for (var x = value - 1; x > 1; x--)
if (value % x == 0) return false;
return true;
}
}
My question is regarding the WaitHandle/ManualResetEvent in the Controller Action. Is there a better way to implement this using Tasks, etc? I am using .NET 4.5.
Without the WaitHandle in place the Action returns before the workflow has completed.
I am familiar with WaitHandle, but it feels like a klunky solution.
Any help / guidance is appreciated.
WaitHandle is an abstract class providing the ability to wait for access to shared resources at the operating system level. If you wish to synchronize access at this level, there is no getting away from using it. However, as you mention, using something like the ManualResetEvent can interrupt the flow of your code, making it hard to read and diagnose when things go wrong.
Many of the recent additions to the .NET framework regarding threading attempt to address this issue. In .NET 4 the notion of Task was introduced, which can streamline the code somewhat, and C# 5 built on top of that infrastructure to introduce the async/await keywords. The code below is a simple console application, showing three ways of achieving what you want using ManualResetEvent, Task's and async/await.
It is important to realize that all three are using the WaitHandle class at some level to synchronize the threads, but the readability is improved using Task's and async/await.
class Program
{
static void Main(string[] args)
{
List<int> results;
//Using raw Wait Handle
ManualResetEvent handle = new ManualResetEvent(false);
Thread thread = new Thread(o =>
{
//Long running process
results = LongRunningTask();
handle.Set();
});
thread.Start();
handle.WaitOne();
Console.WriteLine("Thread completed");
//Using Tasks
Task<List<int>> task = Task<List<int>>.Factory.StartNew(LongRunningTask);
results = task.Result;
Console.WriteLine("Task completed");
//Using async/await
results = LongRunningTaskAsync().Result;
Console.WriteLine("Async Method completed");
Console.ReadLine();
}
public static List<int> LongRunningTask()
{
Thread.Sleep(5000);
return new List<int>();
}
public static async Task<List<int>> LongRunningTaskAsync()
{
return await Task<List<int>>.Factory.StartNew(LongRunningTask);
}
}

Categories