3

I am trying to schedule a unit of work to process in the background, and produce a result. Originally I was thinking about extending the Task class, and scheduling that instance of the custom task. (thinking of Future and Runnable from Java) Each task encapsulates would multiple properties for input/output. Multiple tasks are processed simultaneous, and are completely autonomous.

However, I can't find any examples of doing so, so I am beginning to doubt that that is the correct way to go. Can someone provide the example of doing this correctly with the System.Threading.Tasks

Ian Kemp
  • 28,293
  • 19
  • 112
  • 138
Timur Fanshteyn
  • 2,266
  • 2
  • 23
  • 27
  • Why exactly do you want to extend Task? Maybe you can give an example. I don't see anything in the Q that can't be done already. – H H Aug 01 '10 at 14:09

2 Answers2

2

You do not need to subclass Task. Just instantiate a Task object and give it the method to be executed in the constructor.

The following example computes a value and stores it in a field along with a flag to indicate that the action is complete:

    int _taskResult;
    bool _taskFinished;

    /// <summary>Starts a background task to compute a value and returns immediately.</summary>
    void BeginTask()
    {
        _taskFinished = false;
        Task task = new Task(() =>
        {
            var result = LongComputation();
            lock (this)
            {
                _taskResult = result;
                _taskFinished = true;
            }
        });
        task.Start();
    }

    /// <summary>Performs the long computation. Called from within <see cref="BeginTask"/>.</summary>
    int LongComputation()
    {
        // Insert long computation code here
        return 47;
    }

Of course, in your other code that retrieves the result, you’d have to lock on the same object before checking _taskFinished and _taskResult.

Timwi
  • 65,159
  • 33
  • 165
  • 230
  • This is not as clean as I was hoping, as the result is shared. Considering the code for the computation is complex, it would have to exist in external Task_Like anyway. – Timur Fanshteyn Jul 30 '10 at 21:45
  • 1
    You can store the result any way you want. Your question asked how to use Tasks, and I think I demonstrated that. Of course, you can always ask a separate question if you need further help with something. – Timwi Jul 30 '10 at 21:51
  • I also need to have some custom Task... So this is a good question. We need answers guys! – NoWar Oct 20 '11 at 16:42
  • Wow that is awful code! -1000. 1) You lock on "this", exposing you to unintentional outside locking, see [http://stackoverflow.com/questions/251391/why-is-lockthis-bad]. 2) You leave out the important, complicated part about how to properly and safely wait for the task to be finished. 3) You are basically reimplementing Task. – Søren Boisen Oct 08 '14 at 16:32
-1

You can use another approach as I discovered. You can create the class that encapsulates some Task logic. And outside use ConcurrencyDictionary to manage it. Here is some draft...

 public class ContentManagerTask
{
    public ContentManagerTask(DownloadResult downloadResult)
    {
        TaskResult = downloadResult;
        this.BeginTask(downloadResult);
    }

    public DownloadResult TaskResult;

    /// <summary>Starts a background task to compute a value and returns immediately.</summary>
    private void BeginTask(DownloadResult downloadResult)
    {
        downloadResult.Result = false;
        Task task = new Task(() =>
        {
            var result = Download(downloadResult);
            lock (this)
            {
                TaskResult = result;
            }
        });
        task.Start();
    }

    private DownloadResult Download(DownloadResult downloadResult)
    {
        try
        {
            // Logic to download file
            int i = 0;
            while (i < 10)
            {
                Thread.Sleep(10000);
                i++;
                string log = string.Format("Content Manager: Task - {4} - (Time {0}; ID: {1}) is downloading this File: '{2}' from URL: '{3}' ", DateTime.Now.ToString(), downloadResult.ContentItem.ID, downloadResult.ContentItem.FileName, downloadResult.ContentItem.URL, i.ToString());
                CustomLogger.CustomLogger.WriteNormalLog(log); 
            }

            downloadResult.Result = true;
        }
        catch (Exception ex)
        {
            #region Error
            LogEntry l = new LogEntry();
            l.Message = string.Format("Error: {0}", ex.Message);
            l.Title = "MyApp Error";
            l.Categories.Add(Category.General);
            l.Priority = Priority.Highest;
            l.ExtendedProperties.Add("Method", "DownloadError Download()");

            if (ex.InnerException != null) l.ExtendedProperties.Add("InnerException", ex.InnerException.Message);

            CustomLogger.CustomLogger.WriteErrorLog(l);
            #endregion
            downloadResult.Error = ex;
        }

        return downloadResult;
    }

}

And the outside code

ConcurrentDictionary<Guid, ContentManagerTask> contentManagerWorkArea = new ConcurrentDictionary<Guid, ContentManagerTask>();

private void MyManagerJob()
    {
        bool moreToDo = true;
        while (moreToDo)
        {
            if (contentManagerState)
            {
                Thread.Sleep(1000);

                //var s = sequences.
                #region Start tasks to download content data
                // Each task should check if there is a file. If there is not  a file it should be downloaded. 
                List<ContentItem> contentItems = (List<ContentItem>)App.Current.Properties["ContentItems"];

                foreach (var ci in contentItems)
                {
                    if (ci.IsDownloadable)
                    {
                        // Delete finished tasks

                        var finishedTasks = (from c in contentManagerWorkArea where c.Value.TaskResult.Result == true select c).AsParallel();

                        foreach (var finishedTask in finishedTasks)
                        {
                            ContentManagerTask ctm;
                            contentManagerWorkArea.TryRemove(finishedTask.Key, out ctm);
                            CustomLogger.WriteNormalLog(string.Format("Content Manager: Finished Task has been deleted. Time: {0}; ID: {1}; File: {2}; URL: {3} ", DateTime.Now.ToString(), ctm.TaskResult.ContentItem.ID, ctm.TaskResult.ContentItem.FileName, ctm.TaskResult.ContentItem.URL));

                            ctm = null;
                        }

                        // Add new task

                        var unfinishedTasks = (from c in contentManagerWorkArea where c.Value.TaskResult.Result == false select c).AsParallel();

                        if (unfinishedTasks.Count() == 0) // Area is empty we have to add the first task
                        {
                            DownloadResult dr = new DownloadResult();
                            dr.ContentItem = ci;
                            ContentManagerTask contentManagerTask = new ContentManagerTask(dr);
                            contentManagerWorkArea.TryAdd(ci.ID, contentManagerTask);
                            CustomLogger.WriteNormalLog(string.Format("Content Manager: New Task has been added. Time: {0}; ID: {1}; File: {2}; URL: {3} ", DateTime.Now.ToString(), ci.ID, ci.FileName, ci.URL));
                        }
                        else // Area is not empty and we have to check if some of the tasks are the same we have to insert in
                        {
                            foreach (var unfinishedTask in unfinishedTasks)
                            {
                                if (!unfinishedTask.Value.TaskResult.ContentItem.ID.Equals(ci.ID))
                                {
                                    DownloadResult dr = new DownloadResult();
                                    dr.ContentItem = ci;
                                    ContentManagerTask contentManagerTask = new ContentManagerTask(dr);
                                    contentManagerWorkArea.TryAdd(ci.ID, contentManagerTask);
                                    CustomLogger.WriteNormalLog(string.Format("Content Manager: New Task has been added. Time: {0}; ID: {1}; File: {2}; URL: {3} ", DateTime.Now.ToString(), ci.ID, ci.FileName, ci.URL));
                                }
                            }
                        }
                    }
                }


            }
        }
    }
NoWar
  • 36,338
  • 80
  • 323
  • 498
  • This is really unclean. You are storing the same result object in multiple places, and you assign the same object back and forth redundantly. Since there is only one `DownloadResult` object, you should have only one place to put it; either a field, or a parameter, but not both. – Timwi Oct 25 '11 at 13:48