Programing

Task. 매개 변수로 실행 하시겠습니까?

lottogame 2020. 11. 8. 09:11
반응형

Task. 매개 변수로 실행 하시겠습니까?


저는 멀티 태스킹 네트워크 프로젝트를 진행하고 있으며 Threading.Tasks. 나는 간단한 것을 구현했고 Task.Factory.StartNew()어떻게 할 수 Task.Run()있을까?

다음은 기본 코드입니다.

Task.Factory.StartNew(new Action<object>(
(x) =>
{
    // Do something with 'x'
}), rawData);

개체 브라우저System.Threading.Tasks.Task 에서 살펴본 결과 유사한 매개 변수를 찾을 수 없습니다 . 매개 변수 를 취하는 것만 있고 유형없습니다 .Action<T>Actionvoid

단지 2 가지가있다는 비슷한 : static Task Run(Action action)static Task Run(Func<Task> function)수 있지만 모두 없습니다 포스트 매개 변수 (들).

예, 나는 그것을위한 간단한 확장 메서드를 만들 수 있습니다 알고 있지만 내 주요 질문은 우리가 한 줄에 쓸 수 있습니다Task.Run()?


private void RunAsync()
{
    string param = "Hi";
    Task.Run(() => MethodWithParameter(param));
}

private void MethodWithParameter(string param)
{
    //Do stuff
}

편집하다

대중적인 요구로 인해 Task시작된 것이 호출 스레드와 병렬로 실행 된다는 점에 유의해야합니다 . 기본값이라고 가정하면 TaskScheduler.NET ThreadPool. 어쨌든 이것은 전달되는 매개 변수 Task가 잠재적으로 한 번에 여러 스레드에 의해 액세스되어 공유 상태가되는 것으로 간주 해야 함을 의미합니다 . 여기에는 호출 스레드에서 액세스하는 것이 포함됩니다.

위의 코드에서 그 경우는 완전히 논쟁의 여지가 있습니다. 문자열은 변경할 수 없습니다. 그것이 내가 그것들을 예로 사용한 이유입니다. 그러나 당신이 사용하지 않는다고 String...

한 가지 해결책은 asyncawait. 이것은 기본적으로 SynchronizationContext호출 스레드의 를 캡처하고 호출 후 나머지 메서드에 대한 연속을 await생성하고 생성 된 Task. 이 메서드가 WinForms GUI 스레드에서 실행되는 경우 유형은 WindowsFormsSynchronizationContext.

연속 작업은 캡처 된 항목에 다시 게시 된 후 실행 SynchronizationContext됩니다. 기본적으로 만 다시 실행됩니다. 따라서 await통화 후 시작한 스레드로 돌아갈 것 입니다. 를 사용하는 등 다양한 방법으로이를 변경할 수 있습니다 ConfigureAwait. 즉, 그 방법의 나머지 부분까지 계속되지 않습니다 Task다른 스레드에 완료했습니다. 그러나 호출 스레드는 나머지 메서드가 아닌 병렬로 계속 실행됩니다.

나머지 메소드 실행을 완료하기 위해 기다리는 것은 바람직 할 수도 있고 그렇지 않을 수도 있습니다. 나중에 해당 메서드에서에 전달 된 매개 변수에 액세스하는 것이 없으면 전혀 Task사용하지 않을 수 있습니다 await.

또는 나중에 메서드에서 이러한 매개 변수를 사용할 수도 있습니다. await안전하게 일을 계속할 수 있으므로 즉시 할 이유가 없습니다 . Task반환 된 값을 변수에 저장할 수 await있고 나중에 같은 방법으로도 저장할 수 있습니다 . 예를 들어, 다른 작업을 한 후 전달 된 매개 변수에 안전하게 액세스해야하는 경우입니다. 다시 말하지만, 당신은 할 수 없습니다 필요 awaitTask당신이 그것을 실행하면 바로.

어쨌든 전달 된 매개 변수와 관련하여이 스레드를 안전하게 만드는 간단한 방법 Task.Run은 다음과 같이하는 것입니다.

먼저 장식한다 RunAsyncasync:

private async void RunAsync()

중요 사항

링크 된 문서에서 언급했듯이 표시된 메서드는 void를 반환 하지 않는 것이 좋습니다 . 이에 대한 일반적인 예외는 버튼 클릭 등과 같은 이벤트 핸들러입니다. 그들은 무효를 반환해야합니다. 그렇지 않으면 난 항상 반환하려고 하거나 사용하는 경우 . 몇 가지 이유로 좋은 습관입니다.async TaskTask<TResult>async

이제 아래와 같이 await실행할 Task있습니다. await없이는 사용할 수 없습니다 async.

await Task.Run(() => MethodWithParameter(param));
//Code here and below in the same method will not run until AFTER the above task has completed in one fashion or another

따라서 일반적으로 await작업을 수행하면 매개 변수에 전달 된 매개 변수를 한 번에 여러 스레드에서 수정하는 모든 함정과 함께 잠재적 인 공유 리소스로 취급하는 것을 피할 수 있습니다. 또한 폐쇄에 주의하십시오 . 나는 그것들을 깊이 다루지 않을 것이지만 링크 된 기사는 그것에 대해 훌륭한 일을한다.

사이드 노트

약간의 주제이지만 WinForms GUI 스레드에서으로 표시되어 있으므로 모든 유형의 "차단"을 사용하는 데주의하십시오 [STAThread]. 사용 await하면 전혀 차단되지 않지만 일종의 차단과 함께 사용되는 경우가 있습니다.

"Block"은 기술적으로 WinForms GUI 스레드를 차단할 수 없기 때문에 따옴표로 묶 입니다. 예, 당신이 사용하는 경우 lock윈폼 GUI에이 스레드 합니다 아직도의 "차단"생각 당신에도 불구하고, 메시지를 펌프. 그렇지 않습니다.

이것은 매우 드문 경우에 기괴한 문제를 일으킬 수 있습니다. lock예를 들어 그림을 그릴 때 를 사용하고 싶지 않은 이유 중 하나입니다 . 그러나 그것은 변덕스럽고 복잡한 경우입니다. 그러나 나는 그것이 미친 문제를 일으키는 것을 보았다. 그래서 완전성을 위해 기록했습니다.


변수 캡처를 사용하여 매개 변수를 "전달"합니다.

var x = rawData;
Task.Run(() =>
{
    // Do something with 'x'
});

rawData직접 사용할 수도 있지만 rawData작업 외부의 (예 : for루프 의 반복기 )을 변경하면 작업 내부의 값도 변경되므로주의해야합니다.


나는 이것이 오래된 스레드라는 것을 알고 있지만 수락 된 게시물에 여전히 문제가 있기 때문에 사용해야하는 솔루션을 공유하고 싶었습니다.

문제:

Alexandre Severino가 지적했듯이 param(아래 함수에서) 함수 호출 직후에 변경되면에서 예기치 않은 동작이 발생할 수 있습니다 MethodWithParameter.

Task.Run(() => MethodWithParameter(param)); 

내 솔루션 :

이를 설명하기 위해 다음과 같은 코드를 작성했습니다.

(new Func<T, Task>(async (p) => await Task.Run(() => MethodWithParam(p)))).Invoke(param);

이를 통해 작업을 시작한 후 매개 변수가 매우 빠르게 변경 되었음에도 불구하고 (게시 된 솔루션에 문제가 발생 함) 매개 변수를 비동기 적으로 안전하게 사용할 수있었습니다.

이 접근 방식을 사용하면 param(값 유형)이 전달 된 값을 가져 오므로 param변경 후 비동기 메서드가 실행 되더라도이 코드 줄이 실행되었을 때의 pparam은 그대로 유지됩니다.


이제 다음을 수행 할 수도 있습니다.

Action<int> action = (o) => Thread.Sleep(o);
int param = 10;
await new TaskFactory().StartNew(action, param)

Just use Task.Run

var task = Task.Run(() =>
{
    //this will already share scope with rawData, no need to use a placeholder
});

Or, if you would like to use it in a method and await the task later

public Task<T> SomethingAsync<T>()
{
    var task = Task.Run(() =>
    {
        //presumably do something which takes a few ms here
        //this will share scope with any passed parameters in the method
        return default(T);
    });

    return task;
}

It's unclear if the original problem was the same problem I had: wanting to max CPU threads on computation inside a loop while preserving the iterator's value and keeping inline to avoid passing a ton of variables to a worker function.

for (int i = 0; i < 300; i++)
{
    Task.Run(() => {
        var x = ComputeStuff(datavector, i); // value of i was incorrect
        var y = ComputeMoreStuff(x);
        // ...
    });
}

I got this to work by changing the outer iterator and localizing its value with a gate.

for (int ii = 0; ii < 300; ii++)
{
    System.Threading.CountdownEvent handoff = new System.Threading.CountdownEvent(1);
    Task.Run(() => {
        int i = ii;
        handoff.Signal();

        var x = ComputeStuff(datavector, i);
        var y = ComputeMoreStuff(x);
        // ...

    });
    handoff.Wait();
}

참고URL : https://stackoverflow.com/questions/30225476/task-run-with-parameters

반응형