Gregor Menih
Gregor Menih

Reputation: 5116

Passing variables in Task.Factory

I'm trying to pass a few variables to my thread action using Task.Factory.StartNew();. This is example code:

Task.Factory.StartNew(() =>
    {
        WebClient webClient = new WebClient();
        string source = webClient.DownloadString("http://localhost/?search=" + search_string);
        return source;
    })
.ContinueWith(result =>
    {
        search_string = search.Text;
        search_string = HttpUtility.UrlEncode(search_string, Encoding.UTF8).Replace("+", "%20");
    });

What I want is to pass a groupbox and a panel into the ContinueWith() method, so I can add search results to the panel.

Upvotes: 0

Views: 3059

Answers (3)

Igby Largeman
Igby Largeman

Reputation: 16747

You don't need to pass variables (controls) into the continuation; just access them normally in the continuation code.

But when the thread accesses the controls, you'll get cross-thread exceptions. To prevent that, run the continuation on the UI thread:

.ContinueWith(result => 
    { 
        search_string = search.Text; 
        search_string = HttpUtility.UrlEncode(search_string, Encoding.UTF8).Replace("+", "%20"); 
        // update the UI here (result.Result will contain the return value from the task)        
    }, 
    TaskScheduler.FromCurrentSynchronizationContext());

Passing TaskScheduler.FromCurrentSynchronizationContext() ensures the continuation is dispatched back to the main thread.

Upvotes: 1

Douglas
Douglas

Reputation: 54877

You don’t need to “pass” your UI elements into your ContinueWith method; capturing local variables or even accessing instance variables directly is fine.

However, you do need to ensure that your ContinueWith delegate executes on the UI thread. You may achieve this simply by using an overload that takes a TaskScheduler, specifying the scheduler returned by FromCurrentSynchronizationContext:

Task.Factory.StartNew(() =>
    {
        WebClient webClient = new WebClient();
        string source = webClient.DownloadString(
            "http://localhost/?search=" + search_string);
        return source;
    })
.ContinueWith(antecedent =>
    {
        // Example use of result:
        this.resultTextBox.Result = antecedent.Result;
    },
    TaskScheduler.FromCurrentSynchronizationContext());

Don’t forget that the parameter passed to the ContinueWith delegate is the antecedent task, not its result. To retrieve the result, use its Result property.

Upvotes: 3

JohnB
JohnB

Reputation: 13713

Probably you need not pass them. You can simply reference them within the lambda function. Note that you have to use Invoke or a Dispatcher to access UI elements from a different thread.

Every variable that is "shared" between different lambda functions must be declared outside the lambda function (closure!!!!).

Upvotes: 1

Related Questions