ไม่สามารถระบุตัวปรับแต่ง 'async' ในวิธีการ 'หลัก' ของแอปคอนโซล


445

ฉันใหม่สำหรับการเขียนโปรแกรมแบบอะซิงโครนัสกับโมดิasyncฟายเออร์ ฉันกำลังพยายามหาวิธีเพื่อให้แน่ใจว่าMainวิธีการของฉันในแอปพลิเคชันคอนโซลทำงานแบบอะซิงโครนัสอย่างแท้จริง

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();
    }
}

ฉันรู้ว่านี่ไม่ได้ทำงานแบบอะซิงโครนัสจาก "ด้านบน" เนื่องจากไม่สามารถระบุโมดิasyncฟายเออร์ในMainวิธีการได้ฉันจะรันโค้ดภายในmainแบบอะซิงโครนัสได้อย่างไร


23
นี่ไม่ใช่กรณีใน C # 7.1 อีกต่อไป วิธีการหลักสามารถ async
Vasily Sliounaiev

2
นี่คือC # 7.1 บล็อกโพสต์ประกาศ ดูหัวข้อAsync หลัก
สไตล์

คำตอบ:


382

ดังที่คุณค้นพบใน VS11 คอมไพเลอร์จะไม่อนุญาตให้ใช้async Mainเมธอด สิ่งนี้ได้รับอนุญาต (แต่ไม่เคยแนะนำ) ใน VS2010 ด้วย Async CTP

ฉันมีบล็อกโพสต์ล่าสุดเกี่ยวกับasync / คอยและโปรแกรมคอนโซลแบบอะซิงโครนัสโดยเฉพาะ นี่คือข้อมูลพื้นหลังบางส่วนจากโพสต์แนะนำ:

หาก "คอย" เห็นว่าการรอคอยยังไม่เสร็จสมบูรณ์ มันบอกให้รอที่จะเรียกใช้ส่วนที่เหลือของวิธีการเมื่อมันเสร็จสมบูรณ์แล้วส่งกลับจากวิธีการ async รอยังจะจับบริบทปัจจุบันเมื่อมันผ่านส่วนที่เหลือของวิธีการที่รอ

ในภายหลังเมื่อเสร็จสิ้นการรอคอยมันจะดำเนินการส่วนที่เหลือของวิธีการ async (ภายในบริบทที่จับ)

นี่คือสาเหตุที่ปัญหานี้เกิดขึ้นในโปรแกรมคอนโซลที่มีasync Main:

จำไว้ว่าจากโพสต์บทนำของเราว่าวิธีการซิงค์จะกลับไปที่ผู้โทรก่อนที่จะเสร็จสมบูรณ์ สิ่งนี้ทำงานได้อย่างสมบูรณ์ในแอปพลิเคชั่น UI (วิธีนี้เพิ่งกลับไปที่ลูปเหตุการณ์ UI) และแอปพลิเคชัน ASP.NET (วิธีการส่งคืนเธรด แต่ยังคงคำขอไว้) มันไม่ได้ผลดีนักสำหรับโปรแกรม Console: หลักกลับไปที่ OS - ดังนั้นโปรแกรมของคุณจึงออก

ทางออกหนึ่งคือการให้บริบทของคุณเอง - "วนรอบหลัก" สำหรับโปรแกรมคอนโซลของคุณที่เข้ากันได้กับ async

ถ้าคุณมีเครื่องที่มี Async CTP คุณสามารถใช้GeneralThreadAffineContextจากMy Documents \ Microsoft Visual Studio Async CTP \ Samples (C # Testing) หน่วยทดสอบ หรือคุณสามารถใช้AsyncContextจากแพคเกจ Nito.AsyncEx NuGet ของฉัน

นี่คือตัวอย่างการใช้AsyncContext; GeneralThreadAffineContextมีการใช้งานเกือบเหมือนกัน:

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();
    }
}

หรือคุณสามารถบล็อกเธรดคอนโซลหลักจนกว่างานอะซิงโครนัสของคุณจะเสร็จสมบูรณ์:

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();
    }
}

หมายเหตุการใช้GetAwaiter().GetResult(); นี้หลีกเลี่ยงAggregateExceptionการตัดที่เกิดขึ้นถ้าคุณใช้หรือWait()Result

ปรับปรุง 2017/11/30:ในฐานะของ Visual Studio 2017 ปรับปรุง 3 (15.3) ภาษาในขณะนี้สนับสนุนasync Main- ตราบใดที่มันจะกลับหรือTask Task<T>ดังนั้นตอนนี้คุณสามารถทำสิ่งนี้:

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

ความหมายดูเหมือนจะเหมือนกับGetAwaiter().GetResult()รูปแบบการบล็อกเธรดหลัก อย่างไรก็ตามยังไม่มีข้อมูลจำเพาะภาษาสำหรับ C # 7.1 ดังนั้นนี่เป็นเพียงข้อสมมติฐาน


30
คุณสามารถใช้แบบง่าย ๆWaitหรือResultและก็ไม่มีอะไรผิดปกติ แต่ทราบว่ามีสองแตกต่างที่สำคัญ: 1) ทุกasyncตรันบนสระว่ายน้ำด้ายมากกว่าหัวข้อหลักและ 2) ข้อยกเว้นใด ๆ AggregateExceptionถูกห่อใน
Stephen Cleary

2
มีปัญหาจริงในการหาสิ่งนี้จนกระทั่ง (และโพสต์บล็อกของคุณ) นี่เป็นวิธีที่ง่ายที่สุดในการแก้ปัญหานี้และคุณสามารถติดตั้งแพ็กเกจในคอนโซล nuget ด้วย "แพ็คเกจการติดตั้ง Nito.Asyncex" และคุณทำเสร็จแล้ว
ConstantineK

1
@StephenCleary: ขอบคุณสำหรับการตอบสนองที่รวดเร็วสตีเฟ่น ผมไม่เข้าใจว่าทำไมทุกคนจะไม่ต้องการการดีบักที่จะทำลายเมื่อยกเว้นจะโยน หากฉันกำลังดีบั๊กและทำงานในข้อยกเว้นการอ้างอิงแบบ null การไปที่บรรทัดของโค้ดที่ละเมิดนั้นน่าจะเหมาะสมกว่า VS ทำงานเช่นนั้น "ออกจากกล่อง" สำหรับรหัสซิงโครนัส แต่ไม่ใช่สำหรับ async / ที่รอ
เกร็ก

6
C # 7.1 มี async main ในตอนนี้อาจคุ้มค่ากับคำตอบที่ดีของคุณ @StephenCleary github.com/dotnet/csharplang/blob/master/proposals/csharp-7.1/…
Mafii

3
ถ้าใช้ C # 7.1 รุ่นใน VS 2017 ผมจำเป็นต้องมีเพื่อให้แน่ใจว่าโครงการนี้ได้รับการกำหนดค่าให้ใช้รุ่นล่าสุดของภาษาโดยการเพิ่ม<LangVersion>latest</LangVersion>ลงในไฟล์ csproj ตามที่แสดงไว้ที่นี่
เลียม

359

คุณสามารถแก้ปัญหานี้ได้ด้วยโครงสร้างที่เรียบง่ายนี้:

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

นั่นจะทำให้ทุกสิ่งที่คุณทำบน ThreadPool ตรงตามที่คุณต้องการ (เพื่อให้งานอื่น ๆ ที่คุณเริ่ม / รอไม่พยายามเข้าร่วมเธรดที่พวกเขาไม่ควรทำ) และรอจนกระทั่งทุกอย่างเสร็จสิ้นก่อนที่จะปิดแอปคอนโซล ไม่จำเป็นต้องใช้ลูปพิเศษหรือ libs ภายนอก

แก้ไข: รวมโซลูชันของ Andrew สำหรับข้อยกเว้นที่ไม่ได้ตรวจสอบ


3
วิธีนี้ชัดเจนมาก แต่มีแนวโน้มที่จะตัดข้อยกเว้นดังนั้นฉันจึงมองหาวิธีที่ดีกว่า
abatishchev

2
@abatishchev คุณควรใช้ลอง / จับในรหัสของคุณอย่างน้อยภายใน Task.Run ถ้าไม่ละเอียดมากขึ้นไม่ปล่อยให้ข้อยกเว้นลอยขึ้นไปที่งาน คุณจะหลีกเลี่ยงปัญหาสรุปโดยการลอง / จับสิ่งที่อาจล้มเหลว
Chris Moschini

54
หากคุณแทนที่Wait()ด้วยGetAwaiter().GetResult()คุณจะหลีกเลี่ยงAggregateExceptionเสื้อคลุมเมื่อสิ่งที่โยน
Andrew Arnott

7
นี่คือวิธีที่async mainจะถูกนำมาใช้ใน C # 7.1 ตามที่เขียนนี้
9993

@ user9993 ตามข้อเสนอนี้แสดงว่าไม่เป็นความจริง
Sinjai

90

คุณสามารถทำได้โดยไม่ต้องใช้ไลบรารีภายนอกด้วยการทำสิ่งต่อไปนี้:

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
จำไว้ว่าgetListTask.Resultยังเป็นสายการปิดกั้นและอื่น ๆ Task.WaitAll(getListTask)โค้ดข้างต้นสามารถเขียนได้โดยไม่ต้อง
do0g

27
นอกจากนี้หากGetListโยนคุณจะต้องจับAggregateExceptionและซักถามข้อยกเว้นเพื่อพิจารณาข้อยกเว้นที่เกิดขึ้นจริง อย่างไรก็ตามคุณสามารถโทรGetAwaiter()ที่จะได้รับTaskAwaiterสำหรับTaskและโทรในนั้นคือGetResult() var list = getListTask.GetAwaiter().GetResult();เมื่อได้รับผลจากการที่TaskAwaiter(ยังมีการปิดกั้นการโทร) ข้อยกเว้นใด ๆ AggregateExceptionโยนจะไม่ถูกห่อ
do0g

1
. GetAwaiter () GetResult เป็นคำตอบที่ฉันต้องการ นั่นทำงานได้อย่างสมบูรณ์แบบสำหรับสิ่งที่ฉันพยายามทำ ฉันอาจจะใช้สิ่งนี้ในที่อื่นด้วย
Deathstalker

78

ใน C # 7.1 คุณจะสามารถทำasync Mainได้อย่างเหมาะสม ลายเซ็นที่เหมาะสมสำหรับMainวิธีการได้รับการขยายไปที่:

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

สำหรับเช่นคุณอาจจะทำ:

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

ที่รวบรวมเวลาวิธีการจุดเข้า async GetAwaitor().GetResult()จะได้รับการแปลเป็นโทร

รายละเอียด: https://blogs.msdn.microsoft.com/mazhou/2017/05/30/c-7-series-part-2-async-main

แก้ไข:

ในการเปิดใช้งานคุณสมบัติภาษา C # 7.1 คุณต้องคลิกขวาที่โครงการและคลิก "คุณสมบัติ" จากนั้นไปที่แท็บ "สร้าง" คลิกปุ่มขั้นสูงที่ด้านล่าง:

ป้อนคำอธิบายรูปภาพที่นี่

จากเมนูแบบเลื่อนลงเวอร์ชันภาษาเลือก "7.1" (หรือค่าที่สูงกว่า):

ป้อนคำอธิบายรูปภาพที่นี่

ค่าเริ่มต้นคือ "เวอร์ชันหลักล่าสุด" ซึ่งจะประเมิน (ณ เวลาที่เขียนนี้) เป็น C # 7.0 ซึ่งไม่สนับสนุน async main ในแอปคอนโซล


2
FWIW มีให้ใน Visual Studio 15.3 ขึ้นไปซึ่งขณะนี้มีให้บริการในรุ่นเบต้า / ตัวอย่างจากที่นี่: visualstudio.com/vs/preview
Mahmoud Al-Qudsi

รอสักครู่ ... ฉันกำลังใช้งานการติดตั้งที่อัปเดตอย่างสมบูรณ์และตัวเลือกล่าสุดของฉันคือ 7.1 ... คุณจะได้รับ 7.2 แล้วในเดือนพฤษภาคมได้อย่างไร

คำตอบอาจเป็นของฉัน การแก้ไขเดือนตุลาคมเป็นของคนอื่นตามเวลาที่ฉันคิดว่า 7.2 (ดูตัวอย่าง) อาจมีการเผยแพร่
nawfal

1
มุ่งหน้าไป - ตรวจสอบว่ามันอยู่ใน configs ทั้งหมดไม่ใช่แค่ debug เมื่อคุณทำสิ่งนี้!
230910

1
@ user230910 ขอบคุณ หนึ่งในตัวเลือกที่แปลกประหลาดที่สุดโดย c # team
nawfal

74

ฉันจะเพิ่มคุณสมบัติที่สำคัญที่คำตอบอื่น ๆ ทั้งหมดมองข้าม: การยกเลิก

สิ่งสำคัญอย่างหนึ่งใน TPL คือการสนับสนุนการยกเลิกและแอปคอนโซลมีวิธีการยกเลิกในตัว (CTRL + C) ง่ายมากที่จะรวมเข้าด้วยกัน นี่คือวิธีที่ฉันจัดโครงสร้างแอปคอนโซล async ทั้งหมดของฉัน:

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)
{
    ...
}

โทเค็นการยกเลิกควรถูกส่งไปยังWait()เช่นกันหรือไม่
Siewers

5
ไม่เพราะคุณต้องการให้รหัส async สามารถจัดการกับการยกเลิกได้อย่างงดงาม หากคุณส่งผ่านไปยังรหัสดังWait()กล่าวจะไม่รอให้รหัส async เสร็จสิ้น - จะหยุดรอและสิ้นสุดกระบวนการทันที
คอรีเนลสัน

คุณแน่ใจหรือไม่ ฉันเพิ่งลองและดูเหมือนว่าคำขอยกเลิกจะถูกดำเนินการในระดับที่ลึกที่สุดแม้เมื่อWait()วิธีการผ่านโทเค็นเดียวกัน สิ่งที่ฉันพยายามจะพูดคือมันดูเหมือนจะไม่สร้างความแตกต่าง
Siewers

4
ฉันแน่ใจ. คุณต้องการยกเลิก op เองไม่ใช่รอให้ op ดำเนินการจนเสร็จ ถ้าคุณไม่สนใจเกี่ยวกับการล้างข้อมูลโค้ดหรือผลลัพธ์ของมัน
คอรีเนลสัน

1
ใช่ฉันคิดว่าฉันเข้าใจแล้วมันดูเหมือนจะไม่ได้สร้างความแตกต่างในรหัสของฉัน อีกสิ่งที่ทำให้ฉันออกนอกเส้นทางคือคำแนะนำ ReSharper ที่สุภาพเกี่ยวกับวิธีการรอที่สนับสนุนการยกเลิก;) คุณอาจต้องการรวมลองจับในตัวอย่างเนื่องจากมันจะโยน OperationCancelledException ซึ่งฉันไม่สามารถคิดได้ในตอนแรก
Siewers

22

C # 7.1 (ใช้กับการอัปเดต vs 2017 3) แนะนำหลัก async

คุณสามารถเขียน:

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

สำหรับรายละเอียดเพิ่มเติมC # 7 Series ส่วนที่ 2: หลัก Async

ปรับปรุง:

คุณอาจได้รับข้อผิดพลาดในการรวบรวม:

โปรแกรมไม่มีวิธีการ 'คงที่' ที่เหมาะสมสำหรับจุดเข้าใช้งาน

ข้อผิดพลาดนี้เกิดจากการที่ vs2017.3 มีการกำหนดค่าตามค่าเริ่มต้นเป็น c # 7.0 ไม่ใช่ c # 7.1

คุณควรแก้ไขการตั้งค่าโครงการของคุณอย่างชัดเจนเพื่อตั้งค่าคุณสมบัติ c # 7.1

คุณสามารถตั้งค่า c # 7.1 ได้สองวิธี:

วิธีที่ 1: การใช้หน้าต่างการตั้งค่าโครงการ:

  • เปิดการตั้งค่าของโครงการของคุณ
  • เลือกแท็บสร้าง
  • คลิกปุ่มขั้นสูง
  • เลือกรุ่นที่คุณต้องการตามที่แสดงในรูปต่อไปนี้:

ป้อนคำอธิบายรูปภาพที่นี่

วิธีที่ 2: ปรับเปลี่ยน PropertyGroup ของ. csproj ด้วยตนเอง

เพิ่มคุณสมบัตินี้:

    <LangVersion>7.1</LangVersion>

ตัวอย่าง:

    <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

หากคุณกำลังใช้ C # 7.1 หรือใหม่กว่าไปกับคำตอบ Nawfal ของและเพียงแค่เปลี่ยนประเภทการกลับมาของหลักวิธีการของคุณไปหรือTask Task<int>หากคุณไม่ได้:

  • มีเหมือนโจฮานกล่าวว่าasync Task MainAsync
  • เรียกมัน.GetAwaiter().GetResult()เพื่อจับข้อยกเว้นพื้นฐานเช่น do0g กล่าวกล่าวว่า
  • ยกเลิกการสนับสนุนเช่นคอรีกล่าวว่า
  • วินาทีCTRL+Cควรยุติกระบวนการทันที (ขอบคุณbinki !)
  • จัดการOperationCancelledException- ส่งคืนรหัสข้อผิดพลาดที่เหมาะสม

รหัสสุดท้ายดูเหมือนว่า:

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
โปรแกรมดี ๆ มากมายจะยกเลิก CancelKeyPress ในครั้งแรกเท่านั้นดังนั้นหากคุณกด ^ C เมื่อคุณปิดระบบได้อย่างสง่างาม แต่ถ้าคุณใจร้อนโปรแกรมที่สองจะหยุดทำงาน ด้วยโซลูชันนี้คุณจะต้องฆ่าโปรแกรมด้วยตนเองหากไม่สามารถให้เกียรติแก่ CancellationToken ได้เนื่องจากe.Cancel = trueไม่มีเงื่อนไข
binki

19

ยังไม่ต้องการสิ่งนี้มาก แต่เมื่อฉันใช้แอปพลิเคชันคอนโซลสำหรับการทดสอบอย่างรวดเร็วและจำเป็นต้องใช้ async ฉันเพิ่งแก้ไขได้ดังนี้:

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

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

ตัวอย่างนี้จะทำงานไม่ถูกต้องในกรณีที่คุณต้องกำหนดเวลางานให้กับบริบทปัจจุบันแล้วรอ (ตัวอย่างเช่นคุณสามารถลืมเพิ่ม ConfigureAwait (false) ดังนั้นวิธีการส่งคืนจะถูกกำหนดเวลาในเธรดหลักซึ่งอยู่ในฟังก์ชั่นรอ ) เนื่องจากเธรดปัจจุบันอยู่ในสถานะรอคุณจะได้รับการหยุดชะงัก
Manushin Igor

6
ไม่เป็นความจริง @ManushinIgor อย่างน้อยในตัวอย่างเล็กน้อยนี้ไม่มีการSynchronizationContextเชื่อมโยงกับเธรดหลัก ดังนั้นมันจะไม่หยุดชะงักเพราะแม้จะไม่มีConfigureAwait(false)ความต่อเนื่องทั้งหมดจะดำเนินการบนเธรดพูล
Andrew Arnott



4

เมื่อเปิดตัว C # 5 CTP คุณสามารถทำเครื่องหมาย Main ได้อย่างแน่นอนasync ... แม้ว่าโดยทั่วไปจะไม่แนะนำให้ทำเช่นนั้น ฉันเชื่อว่าสิ่งนี้มีการเปลี่ยนแปลงโดยการเปิดตัว VS 2013 เพื่อเป็นข้อผิดพลาด

หากคุณไม่ได้เริ่มต้นเธรดเบื้องหน้าอื่น ๆโปรแกรมของคุณจะออกเมื่อMainเสร็จสิ้นแม้ว่าจะเริ่มทำงานในพื้นหลังแล้วก็ตาม

คุณมีอะไรจริงๆพยายามที่จะทำอย่างไร? โปรดทราบว่าGetList()วิธีการของคุณไม่จำเป็นต้องเป็นแบบอะซิงโครนัสในขณะนี้ - มันเป็นการเพิ่มเลเยอร์พิเศษโดยไม่มีเหตุผลจริง มันมีเหตุผลเทียบเท่า (แต่ซับซ้อนกว่า):

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

2
จอนฉันต้องการได้รับรายการในรายการแบบอะซิงโครนัสดังนั้นทำไม async จึงไม่เหมาะสมกับวิธี GetList นั้น เป็นเพราะฉันจำเป็นต้องรวบรวมรายการในรายการ async 'และไม่ใช่รายการเอง? เมื่อฉันพยายามทำเครื่องหมายวิธีการหลักด้วย async ฉันได้รับ "ไม่มีวิธีหลักแบบคงที่ ... "
danielovich

@danielovich: DownloadTvChannels()ผลตอบแทนอะไร? สมมุติว่ามันคืน a Task<List<TvChannel>>ใช่ไหม? หากไม่เป็นไปได้ยากที่คุณจะสามารถรอได้ (เป็นไปได้กำหนดรูปแบบ awaiter แต่ไม่น่า.) สำหรับMainวิธีการ - มันยังคงต้องการที่จะเป็นแบบคงที่ ... คุณไม่เปลี่ยนstaticปรับปรุงกับasyncปรับปรุงบางที?
Jon Skeet

ใช่มันจะส่งคืนงาน <.. > เหมือนที่คุณพูด ไม่ว่าฉันจะพยายามใส่ async ในวิธีการหลักมันจะทำให้เกิดข้อผิดพลาด ฉันกำลังนั่งอยู่บนบิตตัวอย่าง VS11!
danielovich

@danielovich: แม้จะมีประเภทคืนเป็นโมฆะ? เพียงแค่public static async void Main() {}? แต่ถ้าDownloadTvChannels()ส่งคืน a อยู่แล้วTask<List<TvChannel>>น่าจะเป็นแบบอะซิงโครนัสแล้วดังนั้นคุณไม่จำเป็นต้องเพิ่มเลเยอร์อื่น มันคุ้มค่าที่จะเข้าใจสิ่งนี้อย่างถี่ถ้วน
Jon Skeet

1
@nawfal: มองย้อนกลับไปฉันคิดว่ามันเปลี่ยนไปก่อนที่จะปล่อย VS2013 ถ้าไม่แน่ใจว่า C # 7 จะมีการเปลี่ยนแปลงที่ ...
จอนสกีต

4

เวอร์ชันใหม่ล่าสุดของ C # - C # 7.1 อนุญาตให้สร้างแอปคอนโซล async ในการเปิดใช้งาน C # 7.1 ในโครงการคุณต้องอัพเกรด VS เป็นอย่างน้อย 15.3 และเปลี่ยนเวอร์ชัน C # เป็นC# 7.1หรือC# latest minor versionหรือในการทำเช่นนี้ไปที่คุณสมบัติโครงการ -> สร้าง -> ขั้นสูง -> รุ่นภาษา

หลังจากนี้รหัสต่อไปนี้จะใช้งานได้:

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

3

บน MSDN เอกสารสำหรับวิธีการ Task.Runแสดงตัวอย่างนี้ซึ่งแสดงวิธีการเรียกใช้เมธอดแบบอะซิงโครนัสจาก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

หมายเหตุข้อความนี้ที่ตามตัวอย่าง:

ตัวอย่างแสดงว่าภารกิจอะซิงโครนัสดำเนินการกับเธรดที่แตกต่างจากเธรดแอปพลิเคชันหลัก

ดังนั้นหากคุณต้องการให้ภารกิจรันบนเธรดแอ็พพลิเคชันหลักให้ดูคำตอบโดย@StephenCleary @StephenCleary

และเกี่ยวกับหัวข้อที่งานดำเนินอยู่ให้สังเกตความคิดเห็นของสตีเฟ่นในคำตอบของเขาด้วย:

คุณสามารถใช้แบบง่าย ๆWaitหรือResultและก็ไม่มีอะไรผิดปกติ แต่ทราบว่ามีสองแตกต่างที่สำคัญ: 1) ทุกasyncตรันบนสระว่ายน้ำด้ายมากกว่าหัวข้อหลักและ 2) ข้อยกเว้นใด ๆ AggregateExceptionถูกห่อใน

(ดูการจัดการข้อยกเว้น (Task Parallel Library)สำหรับวิธีรวมการจัดการข้อยกเว้นเพื่อจัดการกับAggregateException.)


ในที่สุดบน MSDN จากเอกสารสำหรับวิธีการ Task.Delay (TimeSpan)ตัวอย่างนี้แสดงวิธีเรียกใช้งานแบบอะซิงโครนัสที่ส่งคืนค่า:

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

โปรดทราบว่าแทนที่จะส่งผ่านdelegateไปยังTask.Runคุณสามารถส่งผ่านฟังก์ชัน lambda ดังนี้:

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

1

เมื่อต้องการหลีกเลี่ยงการค้างเมื่อคุณเรียกใช้ฟังก์ชันที่กองซ้อนการโทรที่พยายามเข้าร่วมเธรดปัจจุบันอีกครั้ง (ซึ่งติดอยู่ในการรอ) คุณต้องทำสิ่งต่อไปนี้:

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

(นักแสดงจำเป็นต้องแก้ไขความกำกวมเท่านั้น)


ขอบคุณ; Task.Run ไม่ก่อให้เกิดการหยุดชะงักของ GetList () รอคำตอบนี้แสดงว่ามี upvotes มากขึ้น ...
Stefano d'Antonio

1

ในกรณีของฉันฉันมีรายการของงานที่ฉันต้องการเรียกใช้ใน async จากวิธีการหลักของฉันมีการใช้งานนี้ในการผลิตค่อนข้างบางครั้งและทำงานได้ดี

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 ...
}
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.