Argument value different inside called function - c#

I am using the code below for creating multiple tasks in C#:
private static List<Task> _taskList = new List<Task>();
private static ConcurrentQueue<string> cred = new ConcurrentQueue<string>();
private static void TaskMethod1(string usercred)
{
// I am doing a bunch of operations here, all of them can be replaced with
// a sleep for 25 minutes.
// After all operations are done, enqueue again.
cred.Enqueue("usercred")
}
private static void TaskMethod()
{
while(runningService)
{
string usercred;
// This will create more than one task in parallel to run,
// and each task can take up to 30 minutes to finish.
while(cred.TryDequeue(out usercred))
{
_taskList.Add(Task.Run(() => TaskMethod1(usercred)));
}
}
}
internal static void Start()
{
runningService = true;
cred.enqueue("user1");
cred.enqueue("user2");
cred.enqueue("user3");
Task1 = Task.Run(() => TaskMethod());
}
I am encountering a strange behaviour in the code above. By putting a breakpoint at line _taskList.Add(Task.Run(() => TaskMethod1(usercred)));, I am checking value of usercred every time TaskMethod1 is called and it is not null while being called but in one of the cases the value of usercred is null inside TaskMethod1. I have no clue how this could be happening.

You are using Task.Run where in you are using variables from while loop. You are not passing it to the task. So, by the time task executes, its value gets changed.
You should use
while (runningService)
{
string usercred;
// This will create more than one task in parallel to run,
// and each task can take upto 30 minutes to finish.
while (cred.TryDequeue(out usercred))
{
_taskList.Add(Task.Factory.StartNew((data) => TaskMethod1(data.ToString()), usercred)
}
}

You should declare the usercred variable inside the inner while loop, so that the lambda inside the Task.Run captures a separate variable for each loop, and not the same variable for all loops.
while(runningService)
{
while(cred.TryDequeue(out var usercred))
{
_taskList.Add(Task.Run(() => TaskMethod1(usercred)));
}
}
As a side note, I would consider using a BlockingCollection instead of the ConcurrentQueue, to have a way to block the current thread until an item is available, so that I don't have to worry about creating inadvertently a tight loop.

The following change solved the problem.
private static void TaskMethod()
{
while(runningService)
{
string usercred;
// This will create more than one task in parallel to run,
// and each task can take up to 30 minutes to finish.
while(cred.TryDequeue(out usercred))
{
var uc = usercred;
_taskList.Add(Task.Run(() => TaskMethod1(uc)));
}
}
}

Related

Task Inside Loop

I have a windows service with a thread that runs every 2 minutes.
while (true)
{
try
{
repNeg.willExecuteLoopWithTasks(param1, param2, param3);
Thread.Sleep(20000);
}
Inside this I have a loop with tasks:
foreach (RepModel repModelo in listaRep)
{
Task t = new Task(() => { this.coletaFunc(repModelo.EndIp, user, tipoFilial); });
t.Start();
}
But I think this implementation is wrong. I need to only run one task for every element in the list,
and, when a specific task finishes, wait a minute and start again.
M8's I need to say i have 2 situations here.
1 - I can't wait all Task Finish. Because some task can take more then 2 hours to finish and another can take only 27 seconds.
2 - My List of tasks can change. Thats why i got a Thread. Every 2 minutes My thread get a list of Tasks to execute and then start a loop.
But sometimes my Task not Finished yet and another Thread Start Again and then strange things show in my log.
I tryed to use a Dictionry to solve my problem but after some time of execution, sometimes takes days, my log show:
"System.IndexOutOfRangeException"
Here is what I would do...
Create a new class that stores the following (as properties):
a RepModel ID (something unique)
a DateTime for the last time ran
a int for the frequency the task should run in seconds
a bool to determine if the task is in progress or not
Then you need a global list of the class somewhere, say called "JobList".
Your main app should have a Timer, which runs every couple of minutes. The job of this timer is to check for new RepModel (assume these can change over time, i.e a database list). When this ticks, is loops the list and adds any new ones (different ID) to JobList. You may also want to remove any that are no longer required (i.e. removed from DB list).
Then you have a second timer, this runs every second. It's job is to check all items in the JobList and compare the last run time with the current time (and ensure they are not already in progress). If the duration has lapped, then kick off the task. Once the task is complete, update the last run time so it can work next time, ensuring to change the "in progress" flag as you go.
This is all theory and you will need to give it a try yourself, but I think it covers what you are actually trying to achieve.
Some sample code (may or may not compile/work):
class Job
{
public int ID { get; set; }
public DateTime? LastRun { get; set; }
public int Frequency { get; set; }
public bool InProgress { get; set; }
}
List<Job> JobList = new List<Job>();
// Every 2 minutes (or whatever).
void timerMain_Tick()
{
foreach (RepModel repModelo in listaRep)
{
if(!JobList.Any(x => x.ID == repModelo.ID)
{
JobList.Add(new Job(){ ID = repModel.ID, Frequency = 120 });
}
}
}
// Every 10 seconds (or whatever).
void timerTask_Tick()
{
foreach(var job in JobList.Where(x => !x.InProgress && (x.LastRun == null || DateTime.Compare(x.LastRun.AddSeconds(x.Duration), DateTime.Now) < 0))
{
Task t = new Task(() => {
// Do task.
}).ContinueWith(task => {
job.LastRun = DateTime.Now;
job.InProgress = false;
}, TaskScheduler.FromCurrentSynchronizationContext());;
job.InProgress = true;
t.Start();
}
}
So what you really need here is a class that has two operations, it needs to be able to start processing one of your models, and it needs to be able to end processing of one of your models. Separating it from the list will make this easier.
When you start processing a model you'll want to create a CancellationTokenSource to associate with it so that you can stop processing it later. Processing it, in your case, means having a loop, while not cancelled, that runs an operation and then waits a while. Ending the operation is as easy as cancelling the token source.
public class Foo
{
private ConcurrentDictionary<RepModel, CancellationTokenSource> tokenLookup =
new ConcurrentDictionary<RepModel, CancellationTokenSource>();
public async Task Start(RepModel model)
{
var cts = new CancellationTokenSource();
tokenLookup[model] = cts;
while (!cts.IsCancellationRequested)
{
await Task.Run(() => model.DoWork());
await Task.Delay(TimeSpan.FromMinutes(1));
}
}
public void End(RepModel model)
{
CancellationTokenSource cts;
if (tokenLookup.TryRemove(model, out cts))
cts.Cancel();
}
}
If you are using framework 4.0 and more, you may try to benefit from
Parallel.ForEach
Executes a foreach operation in which iterations may run in parallel.
Parallel code may look like this:
Parallel.ForEach(listaRep , repModelo => {
this.coletaFunc(repModelo.EndIp, user, tipoFilial);
});
This will run on multiple cores (if that is possible), and you don't need some specific task sceduler, as your code will wait until all parallel tasks inside parallel loop are finished. And after you can call recursively the same function, if condition was met.

Passing value parameter to Task in c#

I have an issue with passing a long by value to a Task.
I have a list of ID's where I loop through each one, assign to a local variable then pass as a parameter to a new Task. I do not wait for the task to complete before looping round and processing the next ID. I keep an array of Tasks but this is irrelevant.
loop
long ID = list[index];
task[index] = Task.Factory.StartNew(() => doWork(ID));
end loop
If the list contained for example 100 and 200. I would want the first task called with 100
then the second task called with 200. But it does not, doWork receives 200 for both tasks so there is an issue when the value is copied.
I can demonstrate with some simple console code
class Program
{
static void Main(string[] args)
{
long num = 100;
Task one = Task.Factory.StartNew(() => doWork(num));
num = 200;
Console.ReadKey();
}
public static void doWork(long val)
{
Console.WriteLine("Method called with {0}", val);
}
}
The above code will always display
Method called with 200
I modified the code to wait for the task status to switch from WaitingToRun
static void Main(string[] args)
{
long num = 100;
Task one = Task.Factory.StartNew(() => doWork(num));
while(one.Status == TaskStatus.WaitingToRun)
{}
num = 200;
Console.ReadKey();
}
This improves things but not 100% proof, after a few runs I got Method called with 200
Also tried the following
while (true)
{
if (one.Status == TaskStatus.Running | one.IsCompleted == true)
break;
}
but again got 200 displayed.
Any ideas how you can guarantee the value passed to the task without waiting for the task to complete?
Any ideas how you can guarantee the value passed to the task without waiting for the task to complete?
Sure - just create a separate variable which isn't modified anywhere else. You can use a new scope to make that clear:
long num = 100;
Task one;
{
// Nothing can change copyOfNum!
long copyOfNum = num;
one = Task.Factory.StartNew(() => doWork(copyOfNum));
}
You can't change the C# compiler to capture "the value of the variable when delegate is created" rather than capturing the variable, but you can make sure the variable isn't changed afterwards, which accomplishes the same thing.

c# do the equivalent of restarting a Task with some parameter

The main idea here is to fetch some data from somewhere, when it's fetched start writing it, and then prepare the next batch of data to be written, while waiting for the previous write to be complete.
I know that a Task cannot be restarted or reused (nor should it be), although I am trying to find a way to do something like this :
//The "WriteTargetData" method should take the "data" variable
//created in the loop below as a parameter
//WriteData basically do a shedload of mongodb upserts in a separate thread,
//it takes approx. 20-30 secs to run
var task = new Task(() => WriteData(somedata));
//GetData also takes some time.
foreach (var data in queries.Select(GetData))
{
if (task.Status != TaskStatus.Running)
{
//start task with "data" as a parameter
//continue the loop to prepare the next batch of data to be written
}
else
{
//wait for task to be completed
//"restart" task
//continue the loop to prepare the next batch of data to be written
}
}
Any suggestion appreciated ! Thanks. I don't necessarily want to use Task, I just think it might be the way to go.
This may be over simplifying your requirements, but would simply "waiting" for the previous task to complete work for you? You can use Task.WaitAny and Task.WaitAll to wait for previous operations to complete.
pseudo code:
// Method that makes calls to fetch and write data.
public async Task DoStuff()
{
Task currTask = null;
object somedata = await FetchData();
while (somedata != null)
{
// Wait for previous task.
if (currTask != null)
Task.WaitAny(currTask);
currTask = WriteData(somedata);
somedata = await FetchData();
}
}
// Whatever method fetches data.
public Task<object> FetchData()
{
var data = new object();
return Task.FromResult(data);
}
// Whatever method writes data.
public Task WriteData(object somedata)
{
return Task.Factory.StartNew(() => { /* write data */});
}
The Task class is not designed to be restarted. so you Need to create a new task and run the body with the same Parameters. Next i do not see where you start the task with the WriteData function in its body. That will property Eliminate the call of if (task.Status != TaskStatus.Running) There are AFAIK only the class Task and Thread where task is only the abstraction of an action that will be scheduled with the TaskScheduler and executed in different threads ( when we talking about the Common task Scheduler, the one you get when you call TaskFactory.Scheduler ) and the Number of the Threads are equal to the number of Processor Cores.
To you Business App. Why do you wait for the execution of WriteData? Would it be not a lot more easy to gater all data and than submit them into one big Write?
something like ?
public void Do()
{
var task = StartTask(500);
var array = new[] {1000, 2000, 3000};
foreach (var data in array)
{
if (task.IsCompleted)
{
task = StartTask(data);
}
else
{
task.Wait();
task = StartTask(data);
}
}
}
private Task StartTask(int data)
{
var task = new Task(DoSmth, data);
task.Start();
return task;
}
private void DoSmth(object time)
{
Thread.Sleep((int) time);
}
You can use a thread and an AutoResetEvent. I have code like this for several different threads in my program:
These are variable declarations that belong to the main program.
public AutoResetEvent StartTask = new AutoResetEvent(false);
public bool IsStopping = false;
public Thread RepeatingTaskThread;
Somewhere in your initialization code:
RepeatingTaskThread = new Thread( new ThreadStart( RepeatingTaskProcessor ) ) { IsBackground = true; };
RepeatingTaskThread.Start();
Then the method that runs the repeating task would look something like this:
private void RepeatingTaskProcessor() {
// Keep looping until the program is going down.
while (!IsStopping) {
// Wait to receive notification that there's something to process.
StartTask.WaitOne();
// Exit if the program is stopping now.
if (IsStopping) return;
// Execute your task
PerformTask();
}
}
If there are several different tasks you want to run, you can add a variable that would indicate which one to process and modify the logic in PerformTask to pick which one to run.
I know that it doesn't use the Task class, but there's more than one way to skin a cat & this will work.

Task.Factory.StartNew depend on parent thread?

I'm running this thread inside a method from a WCF service library.
The code below is executed at the end of the method. I do this because i don't want the user to wait for a background process to complete that does not affect the output from the WCF to the client.
The problem that i have now is that if i execute that thread and the client gets the response, the parent thread is killed; killing this thread as well. How do i make it so that the parent thread waits for this thread to finish, while performing the rest of the operations?
class Program
{
static void Main(string[] args)
{
Dictionary<string, string> sampleDict = getPopulatedDictionary();
var result = run(sampleDict);
}
public static int run(Dictionary<string, string> sampleDict_)
{
PerformCalculations(sampleDict_);
if (sampleDict_.Keys.Count > 10)
{
System.Threading.Tasks.Task.Factory.StartNew(() =>
{
backgroundprocess(sampleDict_);
});
}
//after returning i still want it to run
return sampleDict_.Keys.Count;
}
private static void backgroundprocess(Dictionary<string,string> dict)
{
foreach (var k in dict.Keys)
{
dict[k] = new Random().Next(2666).ToString();
}
}
}
In short, i want this method to kick off that thread and move onto return the value X but still wait for that thread to finish AFTER it returns the value.
Couldn't you do it as a continuation of the parent task. So execute
FameMappingEntry.SaveFameDBMap(toSaveIdentifiers); as a continuation of a successful completion of the parent task. And then you can wait on the continutation.
var childTask = parentTask.ContinueWith((pt) =>
{
FameMappingEntry.SaveFameDBMap(toSaveIdentifiers);
}, TaskContinuationOptions.OnlyOnRanToCompletion);
And then you can decide if you want to wait on the child task or use another continuation.
If you aren't going to do anything except wait for the background thread to complete, then you might as well just not create the new background thread in the first place and execute the code in-line.
Try this:
var task = System.Threading.Tasks.Task.Factory.StartNew(() =>
{
lock (toSaveIdentifiers)
{
FameMappingEntry.SaveFameDBMap(toSaveIdentifiers);
}
);
int x = dosomething();
task.Wait();
return x;
You should also lock objects in the thread that uses them, and not some other random thread.

Async Lazy Timeout Task

I have an async operation dependent on another server which takes a mostly random amount of time to complete. While the async operation is running there is also processing going on in the 'main thread' which also takes a random amount of time to complete.
The main thread starts the asynchronous task, executes it's primary task, and checks for the result of the asynchronous task at the end.
The async thread pulls data and computes fields which are not critical for the main thread to complete. However this data would be nice to have (and should be included) if the computation is able to complete without slowing down the main thread.
I'd like to setup the async task to run at minimum for 2 seconds, but
to take all the time available between start and end of the main task.
It's a 'lazy timeout' in that it only timeouts if exceeded the 2
second runtime and the result is actually being requested. (The async
task should take the greater of 2 seconds, or the total runtime of the
main task)
EDIT (trying to clarify the requirements): If the async task has had a chance to run for 2 seconds, it shouldn't block the main thread at all. The main thread must allow the async task at least 2 seconds to run. Furthermore, if the main thread takes more than 2 seconds to complete, the async task should be allowed to run as long as the main thread.
I've devised a wrapper that works, however i'd prefer a solution that is actually of type Task. See my wrapper solution below.
public class LazyTimeoutTaskWrapper<tResult>
{
private int _timeout;
private DateTime _startTime;
private Task<tResult> _task;
private IEnumerable<Action> _timeoutActions;
public LazyTimeoutTaskWrapper(Task<tResult> theTask, int timeoutInMillis, System.DateTime whenStarted, IEnumerable<Action> onTimeouts)
{
this._task = theTask;
this._timeout = timeoutInMillis;
this._startTime = whenStarted;
this._timeoutActions = onTimeouts;
}
private void onTimeout()
{
foreach (var timeoutAction in _timeoutActions)
{
timeoutAction();
}
}
public tResult Result
{
get
{
var dif = this._timeout - (int)System.DateTime.Now.Subtract(this._startTime).TotalMilliseconds;
if (_task.IsCompleted ||
(dif > 0 && _task.Wait(dif)))
{
return _task.Result;
}
else
{
onTimeout();
throw new TimeoutException("Timeout Waiting For Task To Complete");
}
}
}
public LazyTimeoutTaskWrapper<tNewResult> ContinueWith<tNewResult>(Func<Task<tResult>, tNewResult> continuation, params Action[] onTimeouts)
{
var result = new LazyTimeoutTaskWrapper<tNewResult>(this._task.ContinueWith(continuation), this._timeout, this._startTime, this._timeoutActions.Concat(onTimeouts));
result._startTime = this._startTime;
return result;
}
}
Does anyone have a better solution than this wrapper?
I'd always start a 2 second task that, when it completes, marks your computation as cancelled . This saves you the strange "diff" time calculation. Here is some code:
Task mainTask = ...; //represents your main "thread"
Task computation = ...; //your main task
Task timeout = TaskEx.Delay(2000);
TaskCompletionSource tcs = new TCS();
TaskEx.WhenAll(timeout, mainTask).ContinueWith(() => tcs.TrySetCancelled());
computation.ContinueWith(() => tcs.TryCopyResultFrom(computation));
Task taskToWaitOn = tcs.Task;
This is pseudo-code. I only wanted to show the technique.
TryCopyResultFrom is meant to copy the computation.Result to the TaskCompletionSource tcs by calling TrySetResult().
Your app just uses taskToWaitOn. It will transition to cancelled after 2s. If the computation completes earlier, it will receive the result of that.
I don't think you can make Task<T> behave this way, because Result is not virtual and there also isn't any other way to change its behavior.
I also think you shouldn't even try to do this. The contract of the Result property is to wait for the result (if it's not available yet) and return it. It's not to cancel the task. Doing that would be very confusing. If you're cancelling the task, I think it should be obvious from the code that you're doing it.
If I were to do this, I would create a wrapper for the Task<T>, but it would look like this:
class CancellableTask<T>
{
private readonly Func<CancellationToken, T> m_computation;
private readonly TimeSpan m_minumumRunningTime;
private CancellationTokenSource m_cts;
private Task<T> m_task;
private DateTime m_startTime;
public CancellableTask(Func<CancellationToken, T> computation, TimeSpan minumumRunningTime)
{
m_computation = computation;
m_minumumRunningTime = minumumRunningTime;
}
public void Start()
{
m_cts = new CancellationTokenSource();
m_task = Task.Factory.StartNew(() => m_computation(m_cts.Token), m_cts.Token);
m_startTime = DateTime.UtcNow;
}
public T Result
{
get { return m_task.Result; }
}
public void CancelOrWait()
{
if (m_task.IsCompleted)
return;
TimeSpan remainingTime = m_minumumRunningTime - (DateTime.UtcNow - m_startTime);
if (remainingTime <= TimeSpan.Zero)
m_cts.Cancel();
else
{
Console.WriteLine("Waiting for {0} ms.", remainingTime.TotalMilliseconds);
bool finished = m_task.Wait(remainingTime);
if (!finished)
m_cts.Cancel();
}
}
}
Note that the computation has a CancellationToken parameter. That's because you can't force cancellation (without dirty tricks like Thread.Abort()) and the computation has to explicitly support it, ideally by executing cancellationToken.ThrowIfCancellationRequested() at appropriate times.

Categories

Resources