c# - कंसोल ऐप की 'मुख्य' विधि पर 'async' संशोधक निर्दिष्ट नहीं कर सकता




.net asynchronous (10)

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

मुझे पता है कि यह "शीर्ष" से असीमित रूप से नहीं चल रहा है। चूंकि Main विधि पर async संशोधक निर्दिष्ट करना संभव नहीं है, इसलिए मैं main रूप से कोड को असीमित रूप से कैसे चला सकता हूं?


अभी तक इसकी बहुत आवश्यकता नहीं है, लेकिन जब मैंने त्वरित परीक्षणों के लिए कंसोल एप्लिकेशन का उपयोग किया है और एसिंक की आवश्यकता है तो मैंने इसे इस तरह हल किया है:

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

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

आप इसे निम्नलिखित करके बाहरी पुस्तकालयों की आवश्यकता के बिना भी कर सकते हैं:

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

एमएसडीएन पर, टास्क.रुन विधि (एक्शन) के लिए प्रलेखन इस उदाहरण को प्रदान करता है जो दिखाता है कि विधि से असीमित रूप से विधि को कैसे चलाया जाए:

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

इस कथन पर ध्यान दें जो उदाहरण का पालन करता है:

उदाहरण दिखाते हैं कि एसिंक्रोनस कार्य मुख्य अनुप्रयोग धागे की तुलना में एक अलग थ्रेड पर निष्पादित करता है।

इसलिए, यदि आप चाहते हैं कि आप मुख्य अनुप्रयोग थ्रेड पर कार्य चलाना चाहते हैं, तो द्वारा उत्तर देखें।

और जिस धागे पर कार्य चलता है, उसके बारे में स्टीफन की comment पर भी ध्यान दें:

आप एक साधारण Wait या Result उपयोग कर सकते हैं, और इसमें कुछ भी गलत नहीं है। लेकिन ध्यान रखें कि दो महत्वपूर्ण मतभेद हैं: 1) सभी async निरंतरता मुख्य धागे की बजाय थ्रेड पूल पर चलती हैं, और 2) किसी अपवाद को एक AggregateException अपवाद में लपेटा जाता है।

( एक्सेप्शन हैंडलिंग (टास्क समांतर लाइब्रेरी) देखें कि एक AggregateException अपवाद से निपटने के लिए अपवाद हैंडलिंग को कैसे शामिल किया जाए।)

अंत में, कार्य के लिए प्रलेखन से एमएसडीएन पर। डेले विधि (टाइमस्पेन) , यह उदाहरण दिखाता है कि एक एसिंक्रोनस कार्य कैसे चलाया जाता है जो मान देता है:

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 को पास करने के बजाय, आप इस तरह एक लैम्ब्डा फ़ंक्शन पास कर सकते हैं:

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

जब सी # 5 सीटीपी पेश किया गया था, तो आप निश्चित रूप से मुख्य को async साथ चिह्नित कर सकते हैं ... हालांकि ऐसा करना एक अच्छा विचार नहीं था। मेरा मानना ​​है कि यह एक त्रुटि बनने के लिए वीएस 2013 के रिलीज द्वारा बदल दिया गया था।

जब तक आप किसी भी अन्य अग्रभूमि धागे को शुरू नहीं कर लेते हैं, तब तक जब आपका Main कार्य पूरा हो जाता है, तब भी आपका प्रोग्राम बाहर निकल जाएगा, भले ही यह कुछ पृष्ठभूमि कार्य शुरू कर दिया हो।

आप वास्तव में क्या करने की कोशिश कर रहे हैं? ध्यान दें कि इस समय आपकी GetList() विधि वास्तव में GetList() होने की आवश्यकता नहीं है - यह किसी वास्तविक कारण के लिए अतिरिक्त परत जोड़ रहा है। यह तार्किक रूप से समतुल्य है (लेकिन इससे अधिक जटिल):

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

फ्रीजिंग से बचने के लिए जब आप कॉल स्टैक के नीचे किसी फ़ंक्शन को कॉल करते हैं जो वर्तमान थ्रेड (जो प्रतीक्षा में फंस जाता है) में फिर से जुड़ने का प्रयास करता है, तो आपको निम्न कार्य करने की आवश्यकता होती है:

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

(कलाकार केवल अस्पष्टता को हल करने के लिए आवश्यक है)


मुख्य रूप से कॉल को GetList पर कॉल करने का प्रयास करें:

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

मेरे मामले में मेरे पास नौकरियों की एक सूची थी जो मैं अपनी मुख्य विधि से एसिंक में भागना चाहता था, काफी समय से उत्पादन में इसका उपयोग कर रहा हूं और ठीक काम करता है।

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

मैं एक महत्वपूर्ण विशेषता जोड़ूंगा कि अन्य सभी उत्तरों ने अनदेखा किया है: रद्दीकरण।

टीपीएल में बड़ी चीजों में से एक रद्दीकरण समर्थन है, और कंसोल ऐप्स में रद्दीकरण की एक विधि है (CTRL + C)। उन्हें एक साथ बांधना बहुत आसान है। इस प्रकार मैं अपने सभी एसिंक कंसोल ऐप्स को कैसे व्यवस्थित करता हूं:

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

सी # - सी # 7.1 का नवीनतम संस्करण एसिंक कंसोल ऐप बनाने की अनुमति देता है। प्रोजेक्ट में सी # 7.1 को सक्षम करने के लिए, आपको अपने वीएस को कम से कम 15.3 में अपग्रेड करना होगा, और सी # संस्करण को C# 7.1 या C# latest minor version बदलना होगा। ऐसा करने के लिए, प्रोजेक्ट गुणों पर जाएं -> बिल्ड -> उन्नत -> भाषा संस्करण।

इसके बाद, कोड निम्नलिखित काम करेगा:

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

सी # 7.1 (बनाम 2017 अपडेट 3 का उपयोग करके) एसिंक मुख्य प्रस्तुत करता है

तुम लिख सकते हो:

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

अधिक जानकारी के लिए सी # 7 श्रृंखला, भाग 2: असिंक मुख्य

अद्यतन करें:

आपको संकलन त्रुटि मिल सकती है:

कार्यक्रम में प्रवेश बिंदु के लिए उपयुक्त एक स्थिर 'मुख्य' विधि नहीं है

यह त्रुटि उस बनाम2017.3 के कारण डिफ़ॉल्ट रूप से कॉन्फ़िगर किया गया है क्योंकि सी # 7.0 सी # 7.1 नहीं है।

सी # 7.1 सुविधाओं को सेट करने के लिए आपको अपनी प्रोजेक्ट की सेटिंग को स्पष्ट रूप से संशोधित करना चाहिए।

आप दो तरीकों से सी # 7.1 सेट कर सकते हैं:

विधि 1: प्रोजेक्ट सेटिंग्स विंडो का उपयोग करना:

  • अपनी परियोजना की सेटिंग्स खोलें
  • बिल्ड टैब का चयन करें
  • उन्नत बटन पर क्लिक करें
  • निम्न संस्करण में दिखाए गए संस्करण का चयन करें जैसा कि आप चाहते हैं:

विधि 2: मैन्युअल रूप से .csproj के PropertyGroup को संशोधित करें

इस संपत्ति को जोड़ें:

    <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>    




c#-5.0