Không thể chỉ định công cụ sửa đổi 'async' trên phương thức 'Chính' của ứng dụng bảng điều khiển


444

Tôi chưa quen với lập trình không đồng bộ với công cụ asyncsửa đổi. Tôi đang cố gắng tìm ra cách để đảm bảo rằng Mainphương pháp ứng dụng bảng điều khiển của tôi thực sự chạy không đồng bộ.

class Program
{
    static void Main(string[] args)
    {
        Bootstrapper bs = new Bootstrapper();
        var list = bs.GetList();
    }
}

public class Bootstrapper {

    public async Task<List<TvChannel>> GetList()
    {
        GetPrograms pro = new GetPrograms();

        return await pro.DownloadTvChannels();
    }
}

Tôi biết điều này không chạy không đồng bộ từ "đầu trang". Vì không thể chỉ định công cụ asyncsửa đổi trên Mainphương thức, làm thế nào tôi có thể chạy mã trong mainkhông đồng bộ?


22
Đây không còn là trường hợp trong C # 7.1. Các phương thức chính có thể không đồng bộ
Vasily Sliounaiev

2
Đây là thông báo bài viết trên blog C # 7.1 . Xem phần có tiêu đề Async Main .
styfle

Câu trả lời:


382

Như bạn đã khám phá, trong VS11 trình biên dịch sẽ không cho phép một async Mainphương thức. Điều này đã được cho phép (nhưng không bao giờ được khuyến nghị) trong VS2010 với Async CTP.

Tôi có các bài đăng trên blog gần đây về các chương trình bảng điều khiển async / awaitkhông đồng bộ nói riêng. Dưới đây là một số thông tin cơ bản từ bài giới thiệu:

Nếu "chờ đợi" thấy rằng việc chờ đợi chưa hoàn thành, thì nó hoạt động không đồng bộ. Nó cho biết có thể chờ đợi để chạy phần còn lại của phương thức khi hoàn thành và sau đó trả về từ phương thức async. Await cũng sẽ nắm bắt bối cảnh hiện tại khi nó vượt qua phần còn lại của phương thức để chờ đợi.

Sau đó, khi chờ đợi hoàn thành, nó sẽ thực thi phần còn lại của phương thức async (trong bối cảnh đã chụp).

Đây là lý do tại sao đây là một vấn đề trong các chương trình Console với async Main:

Hãy nhớ từ bài đăng giới thiệu của chúng tôi rằng một phương thức async sẽ trả về với người gọi trước khi hoàn thành. Điều này hoạt động hoàn hảo trong các ứng dụng UI (phương thức chỉ trả về vòng lặp sự kiện UI) và các ứng dụng ASP.NET (phương thức trả về luồng nhưng vẫn giữ yêu cầu sống). Nó không hoạt động tốt cho các chương trình Console: Trả về chính cho HĐH - vì vậy chương trình của bạn sẽ thoát.

Một giải pháp là cung cấp ngữ cảnh của riêng bạn - một "vòng lặp chính" cho chương trình bảng điều khiển tương thích không đồng bộ.

Nếu bạn có máy có Async CTP, bạn có thể sử dụng GeneralThreadAffineContexttừ My Documents \ Microsoft Visual Studio Async CTP \ Samples (C # tests) Kiểm tra đơn vị \ AsyncTestUtilities . Ngoài ra, bạn có thể sử dụng AsyncContexttừ gói NuGet Nito.AsyncEx của tôi .

Đây là một ví dụ sử dụng AsyncContext; GeneralThreadAffineContextcó cách sử dụng gần như giống hệt nhau:

using Nito.AsyncEx;
class Program
{
    static void Main(string[] args)
    {
        AsyncContext.Run(() => MainAsync(args));
    }

    static async void MainAsync(string[] args)
    {
        Bootstrapper bs = new Bootstrapper();
        var list = await bs.GetList();
    }
}

Ngoài ra, bạn chỉ có thể chặn luồng Bảng điều khiển chính cho đến khi công việc không đồng bộ của bạn hoàn tất:

class Program
{
    static void Main(string[] args)
    {
        MainAsync(args).GetAwaiter().GetResult();
    }

    static async Task MainAsync(string[] args)
    {
        Bootstrapper bs = new Bootstrapper();
        var list = await bs.GetList();
    }
}

Lưu ý việc sử dụng GetAwaiter().GetResult(); Điều này tránh việc AggregateExceptiongói xảy ra nếu bạn sử dụng Wait()hoặcResult .

Cập nhật, 2017-11-30: Kể từ Visual Studio 2017 Update 3 (15.3), ngôn ngữ hiện hỗ trợ một async Main- miễn là nó trả về Taskhoặc Task<T>. Vì vậy, bây giờ bạn có thể làm điều này:

class Program
{
    static async Task Main(string[] args)
    {
        Bootstrapper bs = new Bootstrapper();
        var list = await bs.GetList();
    }
}

Các ngữ nghĩa dường như giống như GetAwaiter().GetResult()phong cách chặn luồng chính. Tuy nhiên, chưa có thông số ngôn ngữ nào cho C # 7.1, vì vậy đây chỉ là giả định.


30
Bạn có thể sử dụng một đơn giản Waithoặc Result, và không có gì sai với điều đó. Nhưng hãy lưu ý rằng có hai điểm khác biệt quan trọng: 1) tất cả các phần asynctiếp theo chạy trên nhóm luồng thay vì luồng chính và 2) mọi trường hợp ngoại lệ được gói trong một AggregateException.
Stephen Cleary

2
Đã có một vấn đề thực sự tìm ra điều này cho đến khi điều này (và bài đăng trên blog của bạn). Đây là phương pháp dễ nhất để giải quyết vấn đề này và bạn có thể cài đặt gói trong bảng điều khiển nuget chỉ với "gói cài đặt Nito.Asyncex" và bạn đã hoàn thành.
ConstantineK

1
@StephenCleary: Cảm ơn vì đã phản ứng nhanh với Stephen. Tôi không hiểu tại sao mọi người sẽ không muốn trình gỡ lỗi phá vỡ khi ném ngoại lệ. Nếu tôi gỡ lỗi và chạy trên một ngoại lệ tham chiếu null, việc chuyển trực tiếp đến dòng mã vi phạm có vẻ được ưu tiên hơn. VS hoạt động như thế "ngoài luồng" cho mã đồng bộ, nhưng không phải là async / await.
Greg

6
C # 7.1 hiện có một async chính, có thể đáng để thêm vào câu trả lời tuyệt vời của bạn,
@StephenCleary

3
Nếu bạn sử dụng phiên bản C # 7.1 trong VS 2017, tôi cần đảm bảo dự án được cấu hình để sử dụng phiên bản mới nhất của ngôn ngữ bằng cách thêm <LangVersion>latest</LangVersion>vào tệp csproj, như được hiển thị ở đây .
Liam

359

Bạn có thể giải quyết điều này với cấu trúc đơn giản này:

class Program
{
    static void Main(string[] args)
    {
        Task.Run(async () =>
        {
            // Do any async anything you need here without worry
        }).GetAwaiter().GetResult();
    }
}

Điều đó sẽ đưa mọi thứ bạn làm ra vào ThreadPool, nơi bạn muốn nó (vì vậy các Nhiệm vụ khác bạn bắt đầu / chờ đợi không cố gắng tham gia lại một Chủ đề mà họ không nên) và đợi cho đến khi mọi thứ hoàn thành trước khi đóng ứng dụng Console. Không cần vòng lặp đặc biệt hoặc libs bên ngoài.

Chỉnh sửa: Kết hợp giải pháp của Andrew cho các trường hợp ngoại lệ chưa được phát hiện.


3
Cách tiếp cận này rất rõ ràng nhưng có xu hướng bao bọc các ngoại lệ vì vậy bây giờ tôi đang tìm kiếm một cách tốt hơn.
abatishchev

2
@abatishchev Bạn nên sử dụng try / Catch trong mã của mình, ít nhất là bên trong Task.Run nếu không chi tiết hơn, không để ngoại lệ nổi lên trong Nhiệm vụ. Bạn sẽ tránh được vấn đề bao bọc bằng cách đặt thử / bắt xung quanh những thứ có thể thất bại.
Chris Moschini

54
Nếu bạn thay thế Wait()bằng GetAwaiter().GetResult()bạn sẽ tránh được AggregateExceptiontrình bao bọc khi mọi thứ ném.
Andrew Arnott

7
Đây là cách async mainđược giới thiệu trong C # 7.1, khi viết bài này.
dùng9993

@ user9993 Theo đề xuất này , điều đó không chính xác.
Sinjai

90

Bạn có thể làm điều này mà không cần các thư viện bên ngoài cũng bằng cách làm như sau:

class Program
{
    static void Main(string[] args)
    {
        Bootstrapper bs = new Bootstrapper();
        var getListTask = bs.GetList(); // returns the Task<List<TvChannel>>

        Task.WaitAll(getListTask); // block while the task completes

        var list = getListTask.Result;
    }
}

7
Hãy nhớ rằng đó getListTask.Resultcũng là một cuộc gọi chặn và do đó, đoạn mã trên có thể được viết mà không có Task.WaitAll(getListTask).
do0g

27
Ngoài ra, nếu GetListném bạn sẽ phải bắt AggregateExceptionvà thẩm vấn các ngoại lệ của nó để xác định ngoại lệ thực tế được ném. Bạn có thể, tuy nhiên, hãy gọi GetAwaiter()để có được TaskAwaitercho Task, và cuộc gọi GetResult()trên đó, tức là var list = getListTask.GetAwaiter().GetResult();. Khi nhận được kết quả từ TaskAwaiter(cũng là một cuộc gọi chặn), mọi ngoại lệ được ném sẽ không được gói trong một AggregateException.
do0g

1
.GetAwaiter (). GetResult là câu trả lời tôi cần. Điều đó hoạt động hoàn hảo cho những gì tôi đã cố gắng để làm. Tôi có thể sẽ sử dụng điều này ở những nơi khác là tốt.
Deathstalker

78

Trong C # 7.1, bạn sẽ có thể thực hiện Chính không đồng bộ chính . Các chữ ký thích hợp cho Mainphương pháp đã được mở rộng thành:

public static Task Main();
public static Task<int> Main();
public static Task Main(string[] args);
public static Task<int> Main(string[] args);

Ví dụ: bạn có thể làm:

static async Task Main(string[] args)
{
    Bootstrapper bs = new Bootstrapper();
    var list = await bs.GetList();
}

Tại thời điểm biên dịch, phương thức điểm nhập không đồng bộ sẽ được dịch để gọi GetAwaitor().GetResult() .

Chi tiết: https://bloss.msdn.microsoft.com/mazhou/2017/05/30/c-7-series-part-2-async-main

BIÊN TẬP:

Để bật các tính năng ngôn ngữ C # 7.1, bạn cần nhấp chuột phải vào dự án và nhấp vào "Thuộc tính", sau đó chuyển đến tab "Xây dựng". Ở đó, nhấp vào nút nâng cao ở phía dưới:

nhập mô tả hình ảnh ở đây

Từ menu thả xuống phiên bản ngôn ngữ, chọn "7.1" (hoặc bất kỳ giá trị nào cao hơn):

nhập mô tả hình ảnh ở đây

Mặc định là "phiên bản chính mới nhất" sẽ đánh giá (tại thời điểm viết bài này) thành C # 7.0, không hỗ trợ async chính trong các ứng dụng bảng điều khiển.


2
FWIW này có sẵn trong Visual Studio 15.3 trở lên, hiện đang có sẵn dưới dạng bản phát hành beta / preview từ đây: visualstudio.com/vs/preview
Mahmoud Al-Qudsi

Đợi một chút ... Tôi đang chạy cài đặt được cập nhật đầy đủ và tùy chọn mới nhất của tôi là 7.1 ... bạn đã nhận được 7.2 vào tháng 5 chưa?

Có thể câu trả lời là của tôi. Chỉnh sửa tháng 10 là bởi người khác vào thời điểm tôi nghĩ 7.2 (xem trước?) Có thể đã được phát hành.
nawfal

1
Cảnh giác - kiểm tra xem nó có trên tất cả các cấu hình không, không chỉ gỡ lỗi khi bạn làm điều này!
user230910

1
@ user230910 cảm ơn. Một trong những lựa chọn kỳ lạ nhất của đội c #.
nawfal

74

Tôi sẽ thêm một tính năng quan trọng mà tất cả các câu trả lời khác đã bỏ qua: hủy bỏ.

Một trong những điều quan trọng trong TPL là hỗ trợ hủy bỏ và các ứng dụng bảng điều khiển có phương thức hủy được tích hợp (CTRL + C). Rất đơn giản để liên kết chúng lại với nhau. Đây là cách tôi cấu trúc tất cả các ứng dụng bảng điều khiển async của mình:

static void Main(string[] args)
{
    CancellationTokenSource cts = new CancellationTokenSource();

    System.Console.CancelKeyPress += (s, e) =>
    {
        e.Cancel = true;
        cts.Cancel();
    };

    MainAsync(args, cts.Token).Wait();
}

static async Task MainAsync(string[] args, CancellationToken token)
{
    ...
}

Mã thông báo hủy có nên được chuyển đến Wait()không?
Người viết

5
Không, bởi vì bạn muốn mã async có thể xử lý việc hủy bỏ một cách duyên dáng. Nếu bạn chuyển nó cho Wait(), nó sẽ không đợi mã async kết thúc - nó sẽ dừng chờ và kết thúc quá trình ngay lập tức.
Cory Nelson

Bạn có chắc chắn về điều đó không? Tôi vừa thử nó và có vẻ như yêu cầu hủy đang được xử lý ở mức sâu nhất, ngay cả khi Wait()phương thức được chuyển cùng một mã thông báo. Điều tôi đang cố nói là, nó dường như không tạo ra sự khác biệt nào.
Người viết

4
Tôi chắc chắn. Bạn muốn tự hủy bỏ op chứ không phải đợi op kết thúc. Trừ khi bạn không quan tâm đến việc hoàn thiện mã dọn dẹp hoặc kết quả của nó.
Cory Nelson

1
Vâng, tôi nghĩ rằng tôi đã nhận được nó, nó dường như không tạo ra bất kỳ sự khác biệt nào trong mã của tôi. Một điều mà ném tôi tắt khóa học là một gợi ý lịch sự ReSharper về phương pháp chờ đợi hỗ trợ hủy bỏ;) Bạn có thể muốn bao gồm một catch trong ví dụ này, vì nó sẽ ném một OperationCancelledException, mà tôi không thể tìm ra lúc đầu
Siewers

22

C # 7.1 (sử dụng so với bản cập nhật 3 2017) giới thiệu async chính

Bạn có thể viết:

   static async Task Main(string[] args)
  {
    await ...
  }

Để biết thêm chi tiết Sê-ri C # 7, Phần 2: Async Main

Cập nhật:

Bạn có thể gặp lỗi biên dịch:

Chương trình không chứa phương thức 'Chính' tĩnh phù hợp với điểm vào

Lỗi này là do vs2017.3 được cấu hình theo mặc định là c # 7.0 chứ không phải c # 7.1.

Bạn nên sửa đổi rõ ràng cài đặt dự án của mình để đặt các tính năng c # 7.1.

Bạn có thể đặt c # 7.1 theo hai phương pháp:

Phương pháp 1: Sử dụng cửa sổ cài đặt dự án:

  • Mở các thiết lập của dự án của bạn
  • Chọn tab Xây dựng
  • Nhấp vào nút Nâng cao
  • Chọn phiên bản bạn muốn Như thể hiện trong hình sau:

nhập mô tả hình ảnh ở đây

Phương thức 2: Sửa đổi nhóm thuộc tính của .csproj theo cách thủ công

Thêm tài sản này:

    <LangVersion>7.1</LangVersion>

thí dụ:

    <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
        <PlatformTarget>AnyCPU</PlatformTarget>
        <DebugSymbols>true</DebugSymbols>
        <DebugType>full</DebugType>
        <Optimize>false</Optimize>
        <OutputPath>bin\Debug\</OutputPath>
        <DefineConstants>DEBUG;TRACE</DefineConstants>
        <ErrorReport>prompt</ErrorReport>
        <WarningLevel>4</WarningLevel>
        <Prefer32Bit>false</Prefer32Bit>
        <LangVersion>7.1</LangVersion>
    </PropertyGroup>    

20

Nếu bạn đang sử dụng C # 7.1 trở lên, hãy đi với câu trả lời của nawfal và chỉ cần thay đổi kiểu trả về của phương thức Chính của bạn thành Taskhoặc Task<int>. Nếu bạn không phải là:

  • Có một async Task MainAsync như Johan nói .
  • Gọi nó .GetAwaiter().GetResult()để bắt ngoại lệ cơ bản như do0g nói .
  • Hỗ trợ hủy bỏ như Cory nói .
  • Một giây CTRL+Cnên chấm dứt quá trình ngay lập tức. (Cảm ơn biley !)
  • Xử lý OperationCancelledException- trả lại mã lỗi thích hợp.

Mã cuối cùng trông như:

private static int Main(string[] args)
{
    var cts = new CancellationTokenSource();
    Console.CancelKeyPress += (s, e) =>
    {
        e.Cancel = !cts.IsCancellationRequested;
        cts.Cancel();
    };

    try
    {
        return MainAsync(args, cts.Token).GetAwaiter().GetResult();
    }
    catch (OperationCanceledException)
    {
        return 1223; // Cancelled.
    }
}

private static async Task<int> MainAsync(string[] args, CancellationToken cancellationToken)
{
    // Your code...

    return await Task.FromResult(0); // Success.
}

1
Nhiều chương trình hay sẽ hủy HủyKeyKey lần đầu tiên để nếu bạn nhấn ^ C một lần bạn sẽ tắt máy một cách duyên dáng nhưng nếu bạn thiếu kiên nhẫn một giây ^ C sẽ chấm dứt một cách vô duyên. Với giải pháp này, bạn cần phải giết chương trình theo cách thủ công nếu nó không tôn trọng CancellingToken vì e.Cancel = truekhông có điều kiện.
binki

19

Chưa cần điều này nhiều, nhưng khi tôi đã sử dụng ứng dụng bảng điều khiển để kiểm tra nhanh và yêu cầu không đồng bộ, tôi đã giải quyết nó như thế này:

class Program
{
    static void Main(string[] args)
    {
        MainAsync(args).Wait();
    }

    static async Task MainAsync(string[] args)
    {
        // Code here
    }
}

Ví dụ này sẽ hoạt động sai trong trường hợp khi bạn phải lên lịch tác vụ cho ngữ cảnh hiện tại và sau đó đợi nó (ví dụ: bạn có thể quên thêm ConfigureAwait (false), vì vậy phương thức return sẽ được lên lịch vào luồng chính, trong hàm Wait ). Vì luồng hiện tại ở trạng thái chờ, bạn sẽ nhận được bế tắc.
Manushin Igor

6
Không đúng, @ManushinIgor. Ít nhất trong ví dụ tầm thường này, không có SynchronizationContextliên kết với luồng chính. Vì vậy, nó sẽ không bế tắc bởi vì ngay cả khi không có ConfigureAwait(false), tất cả các phần tiếp theo sẽ thực hiện trên luồng.
Andrew Arnott


4

Trong Main thử thay đổi cuộc gọi thành GetList thành:

Task.Run(() => bs.GetList());

4

Khi CTP 5 C # được giới thiệu, bạn chắc chắn có thể đánh dấu Main bằngasync ... mặc dù nói chung không nên làm như vậy. Tôi tin rằng điều này đã được thay đổi khi phát hành VS 2013 để trở thành một lỗi.

Trừ khi bạn bắt đầu bất kỳ chủ đề nền trước nào khác , chương trình của bạn sẽ thoát khiMain hoàn thành, ngay cả khi nó bắt đầu một số công việc nền.

Bạn thực sự đang cố gắng làm gì? Lưu ý rằng GetList()phương pháp của bạn thực sự không cần phải đồng bộ hóa vào lúc này - đó là thêm một lớp bổ sung mà không có lý do thực sự. Nó tương đương về mặt logic với (nhưng phức tạp hơn):

public Task<List<TvChannel>> GetList()
{
    return new GetPrograms().DownloadTvChannels();
}

2
Jon, tôi muốn nhận các mục trong danh sách không đồng bộ, vậy tại sao async không phù hợp với phương thức GetList đó? Có phải vì tôi cần thu thập các mục trong danh sách không đồng bộ 'chứ không phải chính danh sách đó? Khi tôi cố gắng đánh dấu phương thức Chính bằng async, tôi nhận được "không chứa phương thức Chính tĩnh ..."
danielovich

@danielovich: Trả DownloadTvChannels()lại cái gì? Có lẽ nó trả lại Task<List<TvChannel>>không? Nếu không, không chắc là bạn có thể chờ đợi nó. (Có thể, được đưa ra mẫu chờ, nhưng không thể.) Đối với Mainphương thức - nó vẫn cần phải tĩnh ... bạn có thể thay thế công cụ staticsửa đổi bằng công cụ asyncsửa đổi không?
Jon Skeet

vâng, nó trả về một Nhiệm vụ <..> như bạn đã nói. Bất kể tôi cố gắng đặt async như thế nào trong chữ ký phương thức chính, nó sẽ báo lỗi. Tôi đang ngồi trên các bit xem trước VS11!
danielovich

@danielovich: Ngay cả với một loại trả lại khoảng trống? Chỉ là public static async void Main() {}? Nhưng nếu DownloadTvChannels()đã trả về a Task<List<TvChannel>>, có lẽ nó đã không đồng bộ - vì vậy bạn không cần thêm lớp khác. Thật đáng để hiểu điều này một cách cẩn thận.
Jon Skeet

1
@nawfal: Nhìn lại, tôi nghĩ nó đã thay đổi trước khi VS2013 được phát hành. Không chắc chắn nếu C # 7 sẽ thay đổi điều đó ...
Jon Skeet

4

Phiên bản mới nhất của C # - C # 7.1 cho phép tạo ứng dụng bảng điều khiển async. Để bật C # 7.1 trong dự án, bạn phải nâng cấp VS của mình lên ít nhất 15.3 và thay đổi phiên bản C # thành C# 7.1hoặcC# latest minor version . Để thực hiện việc này, hãy chuyển đến thuộc tính Project -> Build -> Advanced -> Phiên bản ngôn ngữ.

Sau đó, đoạn mã sau sẽ hoạt động:

internal class Program
{
    public static async Task Main(string[] args)
    {
         (...)
    }

3

Trên MSDN, tài liệu về Phương thức Task.Run (Hành động) cung cấp ví dụ này cho biết cách chạy một phương thức không đồng bộ từ main:

using System;
using System.Threading;
using System.Threading.Tasks;

public class Example
{
    public static void Main()
    {
        ShowThreadInfo("Application");

        var t = Task.Run(() => ShowThreadInfo("Task") );
        t.Wait();
    }

    static void ShowThreadInfo(String s)
    {
        Console.WriteLine("{0} Thread ID: {1}",
                          s, Thread.CurrentThread.ManagedThreadId);
    }
}
// The example displays the following output:
//       Application thread ID: 1
//       Task thread ID: 3

Lưu ý tuyên bố này sau ví dụ:

Các ví dụ cho thấy tác vụ không đồng bộ thực thi trên một luồng khác với luồng ứng dụng chính.

Vì vậy, nếu thay vào đó bạn muốn tác vụ chạy trên luồng ứng dụng chính, hãy xem câu trả lời của @StephenCleary .

Và liên quan đến chủ đề mà nhiệm vụ chạy, cũng lưu ý nhận xét của Stephen về câu trả lời của anh ấy:

Bạn có thể sử dụng một đơn giản Waithoặc Result, và không có gì sai với điều đó. Nhưng hãy lưu ý rằng có hai điểm khác biệt quan trọng: 1) tất cả các phần asynctiếp theo chạy trên nhóm luồng thay vì luồng chính và 2) mọi trường hợp ngoại lệ được gói trong một AggregateException.

(Xem Xử lý ngoại lệ (Thư viện song song tác vụ) để biết cách kết hợp xử lý ngoại lệ để xử lý AggregateException.)


Cuối cùng, trên MSDN từ tài liệu cho Phương thức Task.Delay (TimeSpan) , ví dụ này cho thấy cách chạy một tác vụ không đồng bộ trả về giá trị:

using System;
using System.Threading.Tasks;

public class Example
{
    public static void Main()
    {
        var t = Task.Run(async delegate
                {
                    await Task.Delay(TimeSpan.FromSeconds(1.5));
                    return 42;
                });
        t.Wait();
        Console.WriteLine("Task t Status: {0}, Result: {1}",
                          t.Status, t.Result);
    }
}
// The example displays the following output:
//        Task t Status: RanToCompletion, Result: 42

Lưu ý rằng thay vì truyền a delegateđến Task.Run, thay vào đó bạn có thể truyền hàm lambda như thế này:

var t = Task.Run(async () =>
        {
            await Task.Delay(TimeSpan.FromSeconds(1.5));
            return 42;
        });

1

Để tránh bị đóng băng khi bạn gọi một chức năng ở đâu đó trong ngăn xếp cuộc gọi cố gắng nối lại chuỗi hiện tại (bị kẹt trong Chờ), bạn cần thực hiện như sau:

class Program
{
    static void Main(string[] args)
    {
        Bootstrapper bs = new Bootstrapper();
        List<TvChannel> list = Task.Run((Func<Task<List<TvChannel>>>)bs.GetList).Result;
    }
}

(diễn viên chỉ được yêu cầu để giải quyết sự mơ hồ)


Cảm ơn; Nhiệm vụ.Run không gây ra sự bế tắc của GetList (). Đợi đã, câu trả lời này sẽ có nhiều sự ủng hộ hơn ...
Stefano

1

Trong trường hợp của tôi, tôi đã có một danh sách các công việc mà tôi muốn chạy không đồng bộ từ phương thức chính của mình, đã sử dụng công việc này trong sản xuất khá lâu và hoạt động tốt.

static void Main(string[] args)
{
    Task.Run(async () => { await Task.WhenAll(jobslist.Select(nl => RunMulti(nl))); }).GetAwaiter().GetResult();
}
private static async Task RunMulti(List<string> joblist)
{
    await ...
}
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.