C # හි සමමුහුර්ත ක්‍රමයෙන් අසමමුහුර්ත ක්‍රමය අමතන්නේ කෙසේද?


908

මට තියෙනවා public async void Foo() සමමුහුර්ත ක්‍රමයෙන් ඇමතීමට අවශ්‍ය ක්‍රමයක් තිබේ. එම්එස්ඩීඑන් ප්‍රලේඛනයෙන් මා මෙතෙක් දැක ඇති සියල්ලම අසින්ක් ක්‍රම මගින් අසින්ක් ක්‍රම ලෙස හැඳින්වේ, නමුත් මගේ සම්පූර්ණ වැඩසටහනම අසින්ක් ක්‍රම වලින් ගොඩනගා නැත.

මෙය පවා කළ හැකිද?

අසමමුහුර්ත ක්‍රමයකින් මෙම ක්‍රම ඇමතීමට එක් උදාහරණයක් මෙන්න: http://msdn.microsoft.com/en-us/library/hh300224(v=vs.110).aspx

දැන් මම මෙම සමමුහුර්ත ක්‍රම සමමුහුර්ත ක්‍රම වලින් ඇමතීමට බලාපොරොත්තු වෙමි.


2
මමත් මේකට දිව්වා. RoleProvider අභිබවා යාමෙන් ඔබට GetRolesForUser ක්‍රමයේ අත්සන වෙනස් කළ නොහැක, එවිට ඔබට ක්‍රමවේදය අසමමුහුර්ත කළ නොහැකි අතර ඒ නිසා api අසමමුහුර්තව ඇමතීමට බලා සිටිය නොහැක. මගේ තාවකාලික විසඳුම වූයේ මගේ සාමාන්‍ය HttpClient පන්තියට සමමුහුර්ත ක්‍රම එකතු කිරීමයි, නමුත් මෙය කළ හැකි දැයි දැන ගැනීමට කැමතියි (සහ එහි ඇඟවුම් කුමක් විය හැකිද).
තිමෝති ලී රසල්

2
ඔබේ async void Foo()ක්‍රමය නැවත ලබා නොදෙන නිසා Taskඑයින් අදහස් වන්නේ එය සම්පුර්ණ වූ විට අමතන්නාට දැනගත නොහැකි බැවින් එය නැවත පැමිණිය යුතු Taskබවයි.
ඩයි

1
UI ත්‍රෙඩ් එකක මෙය කරන්නේ කෙසේද යන්න පිළිබඳ අදාළ q / a සම්බන්ධ කිරීම .
Novratio

Answers:


753

අසමමුහුර්ත ක්‍රමලේඛනය කේත පදනම හරහා “වර්ධනය” වේ. එය සොම්බි වෛරසයකට සංසන්දනය කර ඇත . හොඳම විසඳුම එය වර්ධනය වීමට ඉඩ දීමයි, නමුත් සමහර විට එය කළ නොහැකිය.

අර්ධ වශයෙන් අසමමුහුර්ත කේත පදනමක් සමඟ කටයුතු කිරීම සඳහා මම මගේ Nito.AsyncEx පුස්තකාලයේ වර්ග කිහිපයක් ලියා ඇත . සෑම තත්වයක් තුළම ක්‍රියාත්මක වන විසඳුමක් නොමැත.

විසඳුම ඒ

එහි සන්දර්භය වෙත නැවත සමමුහුර්ත කිරීමට අවශ්‍ය නොවන සරල අසමමුහුර්ත ක්‍රමයක් ඔබට තිබේ නම්, ඔබට මෙය භාවිතා කළ හැකිය Task.WaitAndUnwrapException:

var task = MyAsyncMethod();
var result = task.WaitAndUnwrapException();

ඔබට භාවිතා කිරීමට අවශ්‍ය නැතTask.Wait හෝ Task.Resultඒවා ව්‍යතිරේකයන් ඔතා ඇති නිසා AggregateException.

මෙම විසඳුම සුදුසු MyAsyncMethodවන්නේ එහි සන්දර්භය වෙත නැවත සමමුහුර්ත නොකරන්නේ නම් පමණි . වෙනත් වචන වලින් කිවහොත්, සෑම දෙයක්ම අවසන් awaitවිය MyAsyncMethodයුතුය ConfigureAwait(false). මෙයින් අදහස් කරන්නේ එයට කිසිදු UI අංගයක් යාවත්කාලීන කිරීමට හෝ ASP.NET ඉල්ලීම් සන්දර්භයට ප්‍රවේශ විය නොහැකි බවයි.

විසඳුම බී

MyAsyncMethodඑහි සන්දර්භය වෙත නැවත සමමුහුර්ත කිරීමට අවශ්‍ය නම් , ඔබට AsyncContext.RunTaskකැදැලි සන්දර්භයක් සැපයීමට භාවිතා කළ හැකිය :

var result = AsyncContext.RunTask(MyAsyncMethod).Result;

* යාවත්කාලීන කිරීම 4/14/2014: පුස්තකාලයේ නවතම අනුවාද වල API පහත පරිදි වේ:

var result = AsyncContext.Run(MyAsyncMethod);

( Task.Resultමෙම උදාහරණයේ භාවිතා කිරීම කමක් නැත, මන්ද RunTaskඑය ප්‍රචාරය කරනු ඇතTask ව්‍යතිරේකයන් ).

AsyncContext.RunTaskවෙනුවට ඔබට අවශ්‍ය වීමට හේතුව Task.WaitAndUnwrapExceptionවින්ෆෝම්ස් / ඩබ්ලිව්පීඑෆ් / එස්එල් / ඒඑස්පී.නෙට් හි සිදුවිය හැකි තරමක් සියුම් අවහිරතා හැකියාවයි:

  1. සමමුහුර්ත ක්‍රමයක් අසින්ක් ක්‍රමයක් ලෙස හැඳින්වේ, a Task.
  2. සමමුහුර්ත ක්‍රමය මඟින් අවහිර කිරීම බලා සිටී Task.
  3. මෙම asyncක්රමය භාවිතා කරයි awaitතොරව ConfigureAwait.
  4. මෙම Taskවන විට එය පමණක් සම්පූර්ණ නිසා මෙම තත්වය තුල දී සම්පූර්ණ කළ නොහැකි asyncක්රමය නිමි ය; මෙම asyncබැවින් ඒ සඳහා දිගටම පවත්වා සැලසුම් කිරීමට තැත් කිරීම හේතුවෙන් ක්රමය සම්පූර්ණ කළ නොහැකි SynchronizationContext, සහ සම මුහුර්තක ක්රමය දැනටමත් එම සන්දර්භය තුළ ධාවනය වන නිසා, WinForms / WPF / ශ්රී ලංකා / ASP.NET ලකුණු කිරීමට අඛණ්ඩව ඉඩ දෙන්නේ නැහැ.

ConfigureAwait(false)සෑම asyncක්‍රමයක් තුළම හැකිතාක් භාවිතා කිරීම හොඳ අදහසක් වීමට මෙය එක් හේතුවකි .

විසඳුම සී

AsyncContext.RunTaskසෑම අවස්ථාවකම ක්‍රියා නොකරනු ඇත. නිදසුනක් ලෙස, මෙම asyncක්‍රමය සම්පූර්ණ කිරීමට UI සිදුවීමක් අවශ්‍ය වන යමක් බලා සිටී නම්, ඔබ කැදැලි සන්දර්භය සමඟ පවා අවහිර කරනු ඇත. එවැනි අවස්ථාවකදී, ඔබට asyncනූල් තටාකයේ ක්‍රමය ආරම්භ කළ හැකිය :

var task = Task.Run(async () => await MyAsyncMethod());
var result = task.WaitAndUnwrapException();

කෙසේ වෙතත්, මෙම විසඳුම සඳහා MyAsyncMethodනූල් සංචිත සන්දර්භය තුළ ක්‍රියාත්මක වන දෙයක් අවශ්‍ය වේ. එබැවින් එයට UI අංග යාවත්කාලීන කිරීමට හෝ ASP.NET ඉල්ලීම් සන්දර්භයට ප්‍රවේශ විය නොහැක. එවැනි අවස්ථාවකදී, ඔබට ConfigureAwait(false)එහි awaitප්‍රකාශවලට එකතු කළ හැකි අතර විසඳුම A භාවිතා කරන්න.

යාවත්කාලීන කිරීම, 2019-05-01: වර්තමාන "අවම-නරකම පිළිවෙත්" මෙහි MSDN ලිපියක ඇත .


11
විසඳුම A මට අවශ්‍ය දේ සේ පෙනේ, නමුත් එය කාර්යයක් සේ පෙනේ .WaitAndUnwrapException () එය .Net 4.5 RC; එයට ඇත්තේ කාර්යය පමණි. බලා සිටින්න (). නව අනුවාදය සමඟ මෙය කරන්නේ කෙසේද යන්න පිළිබඳ කිසියම් අදහසක් තිබේද? නැතහොත් මෙය ඔබ ලියූ අභිරුචි දිගු කිරීමේ ක්‍රමයක්ද?
මාරාන්තික

3
WaitAndUnwrapExceptionමගේ AsyncEx පුස්තකාලයෙන් මගේම ක්‍රමයයි . නිල .නෙට් ලිබ්ස් සමමුහුර්තකරණය සහ අසින්ක් කේතය මිශ්‍ර කිරීම සඳහා වැඩි උදව්වක් සපයන්නේ නැත (සාමාන්‍යයෙන් ඔබ එය නොකළ යුතුය!). AsyncEx 4.5 මත ධාවනය කිරීමට යාවත්කාලීන කිරීමට පෙර .NET 4.5 RTW සහ නව එක්ස්පී නොවන ලැප්ටොප් පරිගණකයක් සඳහා මම බලා සිටිමි.
ස්ටීවන් ක්ලියරි

15
AsyncContextදැන් Runලැම්බඩා ප්‍රකාශනයක් ගන්නා ක්‍රමයක් ඇත , එබැවින් ඔබ භාවිතා කළ යුතුයvar result = AsyncContext.Run(() => MyAsyncMethod());
ස්ටීවන් ක්ලියරි

1
මම ඔබේ පුස්තකාලය නුජෙට් වෙතින් ඉවත් කර ගත්තෙමි, නමුත් ඇත්ත වශයෙන්ම එයට RunTaskක්‍රමයක් ඇති බවක් නොපෙනේ . මට සොයාගත හැකි සමීපතම දෙය නම් Run, නමුත් එයට Resultදේපලක් නොමැත.
අසාද් සයීදීන්

3
S අසාද්: ඔව්, අවුරුදු 2 කට වඩා පසුව API වෙනස් වී ඇත. ඔබට දැන් සරලව කිව හැකියvar result = AsyncContext.Run(MyAsyncMethod);
ස්ටීවන් ක්ලියරි

330

අවසාන වශයෙන් මගේ ගැටලුව විසඳූ විසඳුමක් එකතු කිරීමෙන් යමෙකුගේ කාලය ඉතිරි වේ.

මුලින්ම ස්ටීවන් ක්ලියරිගේ ලිපි කිහිපයක් කියවන්න :

"අසින්ක් කේතය අවහිර නොකරන්න" හි "හොඳම භාවිතයන් දෙකෙන්", පළමුවැන්න මා වෙනුවෙන් වැඩ නොකළ අතර දෙවැන්න අදාළ නොවේ (මූලික වශයෙන් මට භාවිතා කළ හැකි නම් await, මම කරමි!).

ඒ නිසා මෙහි මගේ සලසන වක් මගක්: ඇතුළේ ඇමතුම ආවරණය කරනවා Task.Run<>(async () => await FunctionAsync());, බලාපොරොත්තු කිසිදු පූර්ණ ඇණ හිටීම තවදුරටත්.

මෙන්න මගේ කේතය:

public class LogReader
{
    ILogger _logger;

    public LogReader(ILogger logger)
    {
        _logger = logger;
    }

    public LogEntity GetLog()
    {
        Task<LogEntity> task = Task.Run<LogEntity>(async () => await GetLogAsync());
        return task.Result;
    }

    public async Task<LogEntity> GetLogAsync()
    {
        var result = await _logger.GetAsync();
        // more code here...
        return result as LogEntity;
    }
}

7
අවුරුදු දෙකකට පසු, මෙම විසඳුම පවත්වා ගෙන යන්නේ කෙසේදැයි දැන ගැනීමට මට කුතුහලයක් ඇත. වෙන ආරංචියක් තියෙනවා ද? නවකයන්ට අහිමි වන මෙම ප්‍රවේශයට සියුම් බවක් තිබේද?
ඩෑන් එස්පර්සා

30
මෙය අවහිර නොවනු ඇත, සත්‍යය, නමුත් එය ආරම්භක නූල් වල සමමුහුර්තකරණ සන්දර්භයෙන් පිටත නව ත්‍රෙඩ් එකක ධාවනය කිරීමට බල කිරීම නිසා ය. කෙසේ වෙතත්, මෙය ඉතා වැරදි ලෙස උපදෙස් දී ඇති ඇතැම් පරිසරයන් ඇත: විශේෂයෙන් වෙබ් යෙදුම්. මෙය වෙබ් සේවාදායකය සඳහා පවතින නූල් effectively ලදායී ලෙස අඩකින් අඩු කළ හැකිය (ඉල්ලීම සඳහා එක් නූල් සහ මේ සඳහා එකක්). ඔබ මෙය වැඩි වැඩියෙන් කරන තරමට එය නරක අතට හැරේ. ඔබේ මුළු වෙබ් සේවාදායකයම අවහිර කිරීම ඔබට අවසන් විය හැකිය.
ක්‍රිස් ප්‍රට්

33
H ක්‍රිස්ප්‍රට් - ඔබ නිවැරදි විය හැකිය, මන්ද Task.Run()එය අසින්ක් කේතයක හොඳම භාවිතයක් නොවන බැවිනි . එහෙත්, නැවතත්, මුල් ප්‍රශ්නයට පිළිතුර කුමක්ද? කිසි විටෙකත් අසමමුහුර්ත ක්‍රමයක් සමමුහුර්තව අමතන්න එපා? අපි ප්‍රාර්ථනා කරමු, නමුත් සැබෑ ලෝකයක, සමහර විට අපට එසේ කිරීමට සිදුවේ.
ටොහිඩ්

1
O ටොහයිඩ් ඔබට ස්ටීවන් ක්ලියරිගේ පුස්තකාලය උත්සාහ කළ හැකිය. මිනිසුන් මෙය උපකල්පනය කර ඇති අතර Parallel.ForEachඅපයෝජනය 'සැබෑ ලෝකයේ' බලපෑමක් ඇති නොකරන බව මම දැක ඇත්තෙමි . මෙම කේතය කොන්සෝලය යෙදුම් සඳහා හරි නමුත් h ක්‍රිස්ප්‍රට් පවසන පරිදි වෙබ් යෙදුම්වල භාවිතා නොකළ යුතුය. එය "දැන්" වැඩ කළ හැකි නමුත් පරිමාණය කළ නොහැකි ය.
makhdumi

1
නව ගිණුම් නිර්මාණය කිරීම ආරම්භ කිරීමට මා තුළ කුතුහලයක් ඇති අතර SO විසින් ප්‍රශ්න වලට පිළිතුරු සැපයීම සඳහා ප්‍රමාණවත් කරුණු ලබා ගැනීම සඳහා ....
Giannis Paraskevopoulos

217

මයික්‍රොසොෆ්ට් විසින් අසින්ක් සමමුහුර්තකරණය ලෙස ක්‍රියාත්මක කිරීම සඳහා අසින්ක් හෙල්පර් (අභ්‍යන්තර) පන්තියක් ගොඩනගා ඇත. මූලාශ්‍රය පෙනේ:

internal static class AsyncHelper
{
    private static readonly TaskFactory _myTaskFactory = new 
      TaskFactory(CancellationToken.None, 
                  TaskCreationOptions.None, 
                  TaskContinuationOptions.None, 
                  TaskScheduler.Default);

    public static TResult RunSync<TResult>(Func<Task<TResult>> func)
    {
        return AsyncHelper._myTaskFactory
          .StartNew<Task<TResult>>(func)
          .Unwrap<TResult>()
          .GetAwaiter()
          .GetResult();
    }

    public static void RunSync(Func<Task> func)
    {
        AsyncHelper._myTaskFactory
          .StartNew<Task>(func)
          .Unwrap()
          .GetAwaiter()
          .GetResult();
    }
}

Microsoft.AspNet.Identity පදනම් පංති වලට ඇත්තේ අසින්ක් ක්‍රම පමණක් වන අතර ඒවා සමමුහුර්ත ලෙස හැඳින්වීම සඳහා දිගටි ක්‍රම සහිත පන්ති ඇත (උදාහරණ භාවිතය):

public static TUser FindById<TUser, TKey>(this UserManager<TUser, TKey> manager, TKey userId) where TUser : class, IUser<TKey> where TKey : IEquatable<TKey>
{
    if (manager == null)
    {
        throw new ArgumentNullException("manager");
    }
    return AsyncHelper.RunSync<TUser>(() => manager.FindByIdAsync(userId));
}

public static bool IsInRole<TUser, TKey>(this UserManager<TUser, TKey> manager, TKey userId, string role) where TUser : class, IUser<TKey> where TKey : IEquatable<TKey>
{
    if (manager == null)
    {
        throw new ArgumentNullException("manager");
    }
    return AsyncHelper.RunSync<bool>(() => manager.IsInRoleAsync(userId, role));
}

කේතයේ බලපත්‍ර නියමයන් ගැන සැලකිලිමත් වන අය සඳහා, මයික්‍රොසොෆ්ට් විසින් එම්අයිටී බලපත්‍ර ලබා ඇති බව දැක්වීමට අදහස් ඇති ඉතා සමාන කේතයකට (නූල් මත සංස්කෘතියට සහය එක් කරයි) සබැඳියක් මෙහි ඇත. https://github.com/aspnet/AspNetIdentity/blob/master/src/Microsoft.AspNet.Identity.Core/AsyncHelper.cs


2
මගේ අසින්ක් ක්‍රම වෙනත් අසින්ක් ක්‍රම බලාපොරොත්තුවෙන් සිටී. මම මගේ awaitඇමතුම් කිසිවක් සරසා නැත ConfigureAwait(false). Global.asax හි ශ්‍රිතයෙන් අසින්ක් ශ්‍රිතයක් AsyncHelper.RunSyncඇමතීමට මම උත්සාහ කළ Application_Start()අතර එය ක්‍රියාත්මක වන බව පෙනේ. AsyncHelper.RunSyncමෙම පෝස්ට් එකේ වෙනත් තැනක මා කියවා ඇති "මාෂල් නැවත අමතන්නාගේ සන්දර්භය වෙත" අවහිර කිරීමේ ගැටලුවට විශ්වාසදායක නොවන බව මින් අදහස් වේද ?
බොබ්.අට්.ඉන්ඩිගෝ.හෙල්ත්

1
Code Bob.at.SBS රඳා පවතින්නේ ඔබ කේත කරන දේ මතය. මම මෙම කේතය භාවිතා කරන්නේ නම් එය ආරක්ෂිත නොවේ . අසමමුහුර්ත විධාන සමමුහුර්තව ක්‍රියාත්මක කිරීම සඳහා මෙය ඉතා අවම හා අර්ධ ආරක්ෂිත ක්‍රමයකි, එය අවහිර කිරීම් ඇති කිරීමට නුසුදුසු ලෙස භාවිතා කළ හැකිය.
එරික් පිලිප්ස්

1
ස්තූතියි. පසු විපරම් ප්‍රශ්න 2: 1) අසින්ක් ක්‍රමයට අවහිරයක් ඇතිවීම වළක්වා ගැනීමට අවශ්‍ය යමක් පිළිබඳ උදාහරණයක් ඔබට දිය හැකිද? 2) මෙම සන්දර්භය තුළ බොහෝ විට කාල පරාසය මත රඳා පවතින අවහිරතා තිබේද? එය ප්‍රායෝගිකව ක්‍රියාත්මක වන්නේ නම්, මගේ කේතය තුළ වේලාව මත රඳා පවතින අවහිරයක් තවමත් තිබිය හැකිද?
බොබ්.අට්ඩිගෝ.හෙල්ත්

. Bob.at.SBS ඉහළ දකුණේ ප්‍රශ්න අසන්න බොත්තම භාවිතා කර ප්‍රශ්න ඇසීමට මම නිර්දේශ කරමි . ඔබට මෙම ප්‍රශ්නයට සබැඳියක් ඇතුළත් කළ හැකිය.
එරික් පිලිප්ස්

1
@ Bob.at ... එරික් විසින් සපයන ලද කේතය Asp යටතේ පරිපූර්ණව ක්‍රියා කරයි. ශුද්ධ mvc5 හා EF6, නමුත් මම වෙනත් විසඳුම් (ConfigureAwait (බොරු) .GetAwaiter () GetResult () හෝ .result.) සම්පූර්ණයෙන්ම මගේ වෙබ් යෙදුම එල්ලා ඇති ඕනෑම උත්සාහ කළ විට
LeonardoX

171

async Main දැන් C # 7.2 හි කොටසක් වන අතර ව්‍යාපෘතිවල උසස් ගොඩනැඟිලි සැකසුම් තුළ එය සක්‍රීය කළ හැකිය.

C # <7.2 සඳහා, නිවැරදි ක්‍රමය:

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


static async Task MainAsync()
{
   /*await stuff here*/
}

මෙය බොහෝ මයික්‍රොසොෆ්ට් ලියකියවිලි වල භාවිතා වන බව ඔබට පෙනෙනු ඇත, උදාහරණයක් ලෙස: https://docs.microsoft.com/en-us/azure/service-bus-messaging/service-bus-dotnet-how-to-use- මාතෘකා-දායකත්වයන්


12
කවුරුහරි මෙය පහත් කළේ ඇයිදැයි මට අදහසක් නැත. මෙය මට බෙහෙවින් ප්‍රයෝජනවත් විය. මෙම විසඳුම නොමැතිව මට සෑම තැනකම ASYCH ප්‍රචාරය කිරීමට සිදුවනු ඇත.
සිරකරුවා ZERO

12
මෙය වඩා හොඳ MainAsync().Wait()ඇයි?
පොඩි කරන්න

9
මම එකඟයි. ඔබට අවශ්‍ය වන්නේ MainAsync (). මේ සියල්ල වෙනුවට රැඳී සිටින්න.
හජ්ජත්

8
r ක්‍රෂ් මම විස්තර කරමින් සිටියේ මෙය සමහර අවහිරතා වළක්වා ගන්නේ කෙසේද යන්නයි. සමහර අවස්ථාවන්හිදී .UI (asp.net) නූලකින් බලා සිටින්න. async deadlocks
ඩේවිඩ්

7
LClintB: ඔබ මෙය ASP.NET Core හි අනිවාර්යයෙන්ම නොකළ යුතුය. වෙබ් යෙදුම් නූල්-සාගින්නෙන් පෙළීමට විශේෂයෙන් අවදානමට ලක්ව ඇති අතර, ඔබ මෙය කරන සෑම අවස්ථාවකම, ඔබ තටාකයෙන් නූල් අදින්නේ වෙනත් ආකාරයකින් ඉල්ලීමක් ඉටු කිරීම සඳහා ය. ඩෙස්ක්ටොප් / ජංගම යෙදුම් සාම්ප්‍රදායිකව තනි පරිශීලකයෙකු බැවින් එය අඩු ගැටළු සහගතය.
ක්‍රිස් ප්‍රට්

50
public async Task<string> StartMyTask()
{
    await Foo()
    // code to execute once foo is done
}

static void Main()
{
     var myTask = StartMyTask(); // call your method which will return control once it hits await
     // now you can continue executing code here
     string result = myTask.Result; // wait for the task to complete to continue
     // use result

}

ඔබ 'බලා සිටින්න' යන පදය කියවා ඇත්තේ "මෙම දිගුකාලීන කාර්යය ආරම්භ කරන්න, පසුව ඇමතුම් ක්‍රමයට පාලනය නැවත ලබා දෙන්න" යනුවෙනි. දිගුකාලීන කාර්යය අවසන් වූ පසු, එය පසුව කේතය ක්‍රියාත්මක කරයි. බලා සිටීමෙන් පසුව ඇති කේතය කෝල්බැක් ක්‍රම වලට සමාන වේ. තාර්කික ප්‍රවාහයට ඇති විශාල වෙනස බාධා නොවන අතර එය ලිවීමට සහ කියවීමට පහසු කරයි.


17
Waitව්‍යතිරේකයන් ඔතා ඇති අතර අවහිරයක් ඇතිවීමේ හැකියාව ඇත.
ස්ටීවන් ක්ලියරි

මම සිතුවේ ඔබ භාවිතා නොකර අසින්ක් ක්‍රමයක් ඇමතුවේ නම් awaitඑය සමමුහුර්තව ක්‍රියාත්මක වනු ඇති බවයි. අවම වශයෙන් එය මට වැඩ කරයි (ඇමතුමක් නොමැතිව myTask.Wait). ඇත්ත වශයෙන්ම, මම ඇමතුමක් ගැනීමට උත්සාහ කළ විට එය ව්‍යතිරේකයක් myTask.RunSynchronously()වූයේ එය දැනටමත් ක්‍රියාත්මක කර ඇති බැවිනි!
විස්මිත

2
මම මේ පිළිතුරට කැමතියි. සංස්කරණය සඳහා හොඳ අදහස්, කුඩා හා අලංකාර. දායක වීමට ස්තූතියි! මම තවමත් සමගාමී මුදල් ඉගෙන ගන්නෙමි, එබැවින් සියල්ල උපකාරී වේ :)
kayleeFrye_onDeck

2
මෙම පිළිතුර අදටත් ක්‍රියාත්මක විය යුතුද? මම එය එම්වීසී රේසර් ව්‍යාපෘතියකින් අත්හදා බැලූ අතර යෙදුම ප්‍රවේශ වීම මත රැඳී තිබේ .Result.
කේතනය කිරීම

8
RTrueBlueAussie එය සමමුහුර්ත කිරීමේ සන්දර්භය අවහිරයයි. ඔබගේ අසින්ක් කේතය නැවත සමමුහුර්ත කිරීමේ සන්දර්භය වෙත මාෂල් කරයි, නමුත් එය එම අවස්ථාවේ Resultඇමතුම මගින් අවහිර කර ඇති බැවින් එය කිසි විටෙකත් එතැනට නොපැමිණේ. එය Resultකිසි විටෙකත් අවසන් නොවේ, මන්දයත් එය අවසන් වන තෙක් බලා සිටින අයෙකු වන නිසා Result, මූලික වශයෙන්: D
Luaan

41

මට 100% විශ්වාස නැත, නමුත් මෙම බ්ලොග් අඩවියේ විස්තර කර ඇති තාක්‍ෂණය බොහෝ තත්වයන් යටතේ ක්‍රියාත්මක විය යුතු යැයි මම විශ්වාස කරමි :

ඔබට task.GetAwaiter().GetResult()මෙම ප්‍රචාරක තර්කනය කෙලින්ම කැඳවීමට අවශ්‍ය නම් මෙය භාවිතා කළ හැකිය .


6
ඉහත ස්ටීවන් ක්ලියරිගේ පිළිතුරෙහි විසඳුම A මෙම ක්‍රමය භාවිතා කරයි. WaitAndUnwrapException ප්‍රභවය බලන්න .
orad

ඔබ අමතන කාර්යය අවලංගු හෝ කාර්යයක් නම් ඔබට GetResult () භාවිතා කිරීමට අවශ්‍යද? මම අදහස් කළේ ඔබට නැවත
ප්‍රති results ල

ඔව්, එසේ නොමැති නම් කාර්යය අවසන් වන තුරු එය අවහිර නොවනු ඇත. GetAwaiter () අමතන්න වෙනුවට විකල්පයක් ලෙස GetResult () ඔබට ඇමතිය හැකිය
.Wait

1
එය "බොහෝ තත්වයන්" කොටසයි. එය සමස්ත නූල් ආකෘතිය මත රඳා පවතින අතර අවහිර වීමේ අවදානමක් තිබේද නැද්ද යන්න තීරණය කිරීම සඳහා වෙනත් නූල් කරන්නේ කුමක්ද.
NStuke

GetAwaiter (). GetResult () තවමත් අවහිර කිරීම් ඇති කළ හැකිය. එය ව්‍යතිරේකය වඩාත් සංවේදී එකක් බවට පත් කරයි.
nawfal

25

කෙසේ වෙතත්, සෑම තත්වයක් තුළම ක්‍රියාත්මක වන හොඳ විසඳුමක් ඇත (පාහේ: අදහස් බලන්න): තාවකාලික පණිවිඩ පොම්පයක් (සමමුහුර්තකරණය කොන්ටෙක්ස්ට්).

ඇමතුම් නූල් අපේක්ෂිත පරිදි අවහිර කරනු ඇති අතර, අසින්ක් ශ්‍රිතයෙන් කැඳවනු ලබන සියලුම අඛණ්ඩතාවන් අවහිර නොවන බවට සහතික වන අතර ඒවා ඇමතුම් නූල් මත ධාවනය වන තාවකාලික සමමුහුර්තකරණයට (පණිවිඩ පොම්පයට) මාෂල් කරනු ඇත.

තාවකාලික පණිවිඩ පොම්ප සහායකයාගේ කේතය:

using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;

namespace Microsoft.Threading
{
    /// <summary>Provides a pump that supports running asynchronous methods on the current thread.</summary>
    public static class AsyncPump
    {
        /// <summary>Runs the specified asynchronous method.</summary>
        /// <param name="asyncMethod">The asynchronous method to execute.</param>
        public static void Run(Action asyncMethod)
        {
            if (asyncMethod == null) throw new ArgumentNullException("asyncMethod");

            var prevCtx = SynchronizationContext.Current;
            try
            {
                // Establish the new context
                var syncCtx = new SingleThreadSynchronizationContext(true);
                SynchronizationContext.SetSynchronizationContext(syncCtx);

                // Invoke the function
                syncCtx.OperationStarted();
                asyncMethod();
                syncCtx.OperationCompleted();

                // Pump continuations and propagate any exceptions
                syncCtx.RunOnCurrentThread();
            }
            finally { SynchronizationContext.SetSynchronizationContext(prevCtx); }
        }

        /// <summary>Runs the specified asynchronous method.</summary>
        /// <param name="asyncMethod">The asynchronous method to execute.</param>
        public static void Run(Func<Task> asyncMethod)
        {
            if (asyncMethod == null) throw new ArgumentNullException("asyncMethod");

            var prevCtx = SynchronizationContext.Current;
            try
            {
                // Establish the new context
                var syncCtx = new SingleThreadSynchronizationContext(false);
                SynchronizationContext.SetSynchronizationContext(syncCtx);

                // Invoke the function and alert the context to when it completes
                var t = asyncMethod();
                if (t == null) throw new InvalidOperationException("No task provided.");
                t.ContinueWith(delegate { syncCtx.Complete(); }, TaskScheduler.Default);

                // Pump continuations and propagate any exceptions
                syncCtx.RunOnCurrentThread();
                t.GetAwaiter().GetResult();
            }
            finally { SynchronizationContext.SetSynchronizationContext(prevCtx); }
        }

        /// <summary>Runs the specified asynchronous method.</summary>
        /// <param name="asyncMethod">The asynchronous method to execute.</param>
        public static T Run<T>(Func<Task<T>> asyncMethod)
        {
            if (asyncMethod == null) throw new ArgumentNullException("asyncMethod");

            var prevCtx = SynchronizationContext.Current;
            try
            {
                // Establish the new context
                var syncCtx = new SingleThreadSynchronizationContext(false);
                SynchronizationContext.SetSynchronizationContext(syncCtx);

                // Invoke the function and alert the context to when it completes
                var t = asyncMethod();
                if (t == null) throw new InvalidOperationException("No task provided.");
                t.ContinueWith(delegate { syncCtx.Complete(); }, TaskScheduler.Default);

                // Pump continuations and propagate any exceptions
                syncCtx.RunOnCurrentThread();
                return t.GetAwaiter().GetResult();
            }
            finally { SynchronizationContext.SetSynchronizationContext(prevCtx); }
        }

        /// <summary>Provides a SynchronizationContext that's single-threaded.</summary>
        private sealed class SingleThreadSynchronizationContext : SynchronizationContext
        {
            /// <summary>The queue of work items.</summary>
            private readonly BlockingCollection<KeyValuePair<SendOrPostCallback, object>> m_queue =
                new BlockingCollection<KeyValuePair<SendOrPostCallback, object>>();
            /// <summary>The processing thread.</summary>
            private readonly Thread m_thread = Thread.CurrentThread;
            /// <summary>The number of outstanding operations.</summary>
            private int m_operationCount = 0;
            /// <summary>Whether to track operations m_operationCount.</summary>
            private readonly bool m_trackOperations;

            /// <summary>Initializes the context.</summary>
            /// <param name="trackOperations">Whether to track operation count.</param>
            internal SingleThreadSynchronizationContext(bool trackOperations)
            {
                m_trackOperations = trackOperations;
            }

            /// <summary>Dispatches an asynchronous message to the synchronization context.</summary>
            /// <param name="d">The System.Threading.SendOrPostCallback delegate to call.</param>
            /// <param name="state">The object passed to the delegate.</param>
            public override void Post(SendOrPostCallback d, object state)
            {
                if (d == null) throw new ArgumentNullException("d");
                m_queue.Add(new KeyValuePair<SendOrPostCallback, object>(d, state));
            }

            /// <summary>Not supported.</summary>
            public override void Send(SendOrPostCallback d, object state)
            {
                throw new NotSupportedException("Synchronously sending is not supported.");
            }

            /// <summary>Runs an loop to process all queued work items.</summary>
            public void RunOnCurrentThread()
            {
                foreach (var workItem in m_queue.GetConsumingEnumerable())
                    workItem.Key(workItem.Value);
            }

            /// <summary>Notifies the context that no more work will arrive.</summary>
            public void Complete() { m_queue.CompleteAdding(); }

            /// <summary>Invoked when an async operation is started.</summary>
            public override void OperationStarted()
            {
                if (m_trackOperations)
                    Interlocked.Increment(ref m_operationCount);
            }

            /// <summary>Invoked when an async operation is completed.</summary>
            public override void OperationCompleted()
            {
                if (m_trackOperations &&
                    Interlocked.Decrement(ref m_operationCount) == 0)
                    Complete();
            }
        }
    }
}

භාවිතය:

AsyncPump.Run(() => FooAsync(...));

අසින්ක් පොම්පය පිළිබඳ වඩාත් සවිස්තරාත්මක විස්තරයක් මෙහි ඇත.


ව්‍යතිරේක සන්දර්භය සහ AsyncPump stackoverflow.com/questions/23161693/…
PreguntonCojoneroCabrón

1
ඔබට අහඹු ලෙස HttpContext.Current අහිමි විය හැකි බැවින් මෙය Asp.net දර්ශනයක ක්‍රියා නොකරයි.
ජොෂ් මූච්

13

මෙම ප්‍රශ්නයට තවදුරටත් අවධානය යොමු කරන ඕනෑම කෙනෙකුට ...

ඔබ බැලුවහොත් Microsoft.VisualStudio.Services.WebApiපංතියක් TaskExtensionsඇත. එම පංතිය තුළ ඔබට ස්ථිතික විස්තාරණ ක්‍රමය පෙනෙනු ඇත Task.SyncResult(), එමඟින් කාර්යය නැවත පැමිණෙන තෙක් නූල් මුළුමනින්ම අවහිර කරයි.

අභ්‍යන්තරව එය අමතන්නේ task.GetAwaiter().GetResult()ඉතා සරල ය, කෙසේ වෙතත් asyncනැවත පැමිණෙන ඕනෑම ක්‍රමවේදයක් මත වැඩ කිරීම සඳහා එය අධික ලෙස පටවා ඇත Task, Task<T>හෝTask<HttpResponseMessage> ... සින්ටැක්ටික් සීනි, බබා ... තාත්තාට මිහිරි දතක් තිබේ.

...GetAwaiter().GetResult()අවහිර කිරීමේ සන්දර්භය තුළ අසින්ක් කේතය ක්‍රියාත්මක කිරීමට එම්එස්-නිල ක්‍රමය ලෙස පෙනේ . මගේ භාවිත නඩුව සඳහා ඉතා හොඳින් ක්‍රියා කරන බව පෙනේ.


3
ඔබ මාව "මුළුමනින්ම අවහිර කළාක් මෙන්" තබා ඇත.
ඩාවුඩ් ඉබ්න් කරීම්

GetResult () සෑම විටම මට අවහිරතා ඇති කරයි.
අයිඩාන්

10
var result = Task.Run(async () => await configManager.GetConfigurationAsync()).ConfigureAwait(false);

OpenIdConnectConfiguration config = result.GetAwaiter().GetResult();

නැතහොත් මෙය භාවිතා කරන්න:

var result=result.GetAwaiter().GetResult().AccessToken

6

ඔබට ඕනෑම සමමුහුර්ත ක්‍රමයක් සමමුහුර්ත කේතයෙන් ඇමතිය හැකිය, එනම් ඔබට ඒවා අවශ්‍ය වන තෙක් awaitඒවා සලකුණු කළ යුතුය.async .

බොහෝ අය මෙහි යෝජනා කරන පරිදි, ඔබේ සමමුහුර්ත ක්‍රමයේ ප්‍රති task ලයක් ලෙස ඔබට රැඳී සිටින්න () හෝ ප්‍රති ult ලය අමතන්න, නමුත් එවිට ඔබ අවසන් වන්නේ එම ක්‍රමයේ අවහිර කිරීමේ ඇමතුමකිනි, කුමන ආකාරයේ අසමමුහුර්ත අරමුණ පරාජය කරයි.

මට ඔබට සැබවින්ම ඔබේ ක්‍රමය සෑදිය නොහැකි asyncඅතර සමමුහුර්ත ක්‍රමය අගුළු දැමීමට ඔබට අවශ්‍ය නැත, එවිට ඔබට කර්තව්‍යයේ අඛණ්ඩ විත් ක්‍රමයට පරාමිතියක් ලෙස ඇමතුමක් ලබා දීමේ ක්‍රමයක් භාවිතා කිරීමට සිදුවේ.


5
එවිට එය දැන් ක්‍රමයට සමකාලීනව නොකියනු ඇත?
ජෙෆ් මර්කාඩෝ

2
මට තේරෙන පරිදි, ප්‍රශ්නය වූයේ ඔබට අසින්ක් නොවන ක්‍රමයකින් අසින්ක් ක්‍රමයක් ඇමතිය හැකිද යන්නයි. මෙයින් අසමමුහුර්ත ක්‍රමය අවහිර වන ආකාරයෙන් ඇමතීමට අදහස් නොකෙරේ.
base2

කණගාටුයි, ඔබගේ "ඒවාද සලකුණු කළ යුතුය async" ඔබ සැබවින්ම පවසන දෙයින් මගේ අවධානය ඉවතට යොමු විය.
ජෙෆ් මර්කාඩෝ

මම අසමමුහුර්ත භාවය ගැන සැබවින්ම සැලකිල්ලක් නොදක්වන්නේ නම්, එය මේ ආකාරයෙන් හැඳින්වීම හරිද? (සහ ස්ටීවන් ක්ලියරි දිගින් දිගටම නොසැලකිලිමත් වන ඔතා ඇති ව්‍යතිරේකවල ඇති අවහිරතා ගැන කුමක් කිව හැකිද?) මට පරීක්ෂණ ක්‍රම කිහිපයක් තිබේ (එය සමමුහුර්තව ක්‍රියාත්මක කළ යුතුය) අසමමුහුර්ත ක්‍රම පරීක්ෂා කරන. ඉදිරියට යාමට පෙර මම ප්‍රති result ලය එනතෙක් බලා සිටිය යුතුය, එබැවින් අසමමුහුර්ත ක්‍රමයේ ප්‍රති result ලය පරීක්ෂා කළ හැකිය.
විස්මිත

3

පැය ගණනක් විවිධ ක්‍රම අත්හදා බැලීමෙන් පසු, වැඩි හෝ අඩු සාර්ථකත්වයක් සහිතව, මෙය මම අවසන් කළෙමි. ප්‍රති result ල ලබා ගැනීමේදී එය අවහිරයකින් අවසන් නොවන අතර එය මුල් ව්‍යතිරේකය ලබාගෙන විසි කරයි.

private ReturnType RunSync()
{
  var task = Task.Run(async () => await myMethodAsync(agency));
  if (task.IsFaulted && task.Exception != null)
  {
    throw task.Exception;
  }

  return task.Result;
}

1
ආපසු පැමිණීමේ කාර්යය සමඟ ක්‍රියා කරයි. GetAwaiter () GetResult ();
පර් ජී

ප්‍රති Res ල මම සිතන ආකාරයටම මූලික වශයෙන් සමාන වේ .GetAwaiter (). GetResult ()
G සඳහා

සුපුරුදු නිසා එය එසේ නොවේ .එය බලා නොසිට ප්‍රති result ලය අවහිරයකට තුඩු දිය හැකිය.
Jiří Herník

මෙය මට අවහිරතා ඇති කරයි, සමාවෙන්න. අසින්ක් හෙල්පර් පිළිතුර එකම නොවන බව පෙනේ.
අයිඩාන්

2

වෙනත් පිළිතුරු වලින් දේවානුභාවයෙන්, මම පහත සරල උපකාරක ක්‍රම නිර්මාණය කළෙමි:

public static TResult RunSync<TResult>(Func<Task<TResult>> method)
{
    var task = method();
    return task.GetAwaiter().GetResult();
}

public static void RunSync(Func<Task> method)
{
    var task = method();
    task.GetAwaiter().GetResult();
}

ඒවා පහත පරිදි හැඳින්විය හැකිය (ඔබ වටිනාකමක් ආපසු ලබා දෙනවාද නැද්ද යන්න මත පදනම්ව):

RunSync(() => Foo());
var result = RunSync(() => FooWithResult());

මුල් ප්‍රශ්නයේ අත්සන public async void Foo()වැරදිය. එය public async Task Foo()ඔබ ආපසු එවිය යුතු ආකාරයටම වටිනාකමක් ලබා නොදෙන අසින්ක් ක්‍රම සඳහා කාර්යය අවලංගු නොවේ (ඔව්, දුර්ලභ ව්‍යතිරේක කිහිපයක් තිබේ).


-3

එම කවුළු අසින්ක් ක්‍රමයට AsTask () නමින් කුඩා කුඩා ක්‍රමයක් ඇත. ඔබට මෙය භාවිතා කර ක්‍රමවේදය නැවත කාර්යයක් ලෙස නැවත ලබා ගත හැකි වන අතර එමඟින් ඔබට එය මත රැඳී සිටින්න () අමතන්න.

උදාහරණයක් ලෙස, වින්ඩෝස් දුරකථන 8 සිල්වර් ලයිට් යෙදුමක, ඔබට පහත සඳහන් දෑ කළ හැකිය:

private void DeleteSynchronous(string path)
{
    StorageFolder localFolder = Windows.Storage.ApplicationData.Current.LocalFolder;
    Task t = localFolder.DeleteAsync(StorageDeleteOption.PermanentDelete).AsTask();
    t.Wait();
}

private void FunctionThatNeedsToBeSynchronous()
{
    // Do some work here
    // ....

    // Delete something in storage synchronously
    DeleteSynchronous("pathGoesHere");

    // Do other work here 
    // .....
}

මෙය උපකාරී වේ යැයි සිතමු!


-5

ඔබට එය ක්‍රියාත්මක කිරීමට අවශ්‍ය නම් සමමුහුර්ත කරන්න

MethodAsync().RunSynchronously()

4
මෙම ක්‍රමය සීතල කාර්යයන් ආරම්භ කිරීම සඳහා අදහස් කෙරේ. සාමාන්‍යයෙන් අසින්ක් ක්‍රම මඟින් උණුසුම් කාර්යයක් නැවත ලබා දෙනු ඇත, වෙනත් වචන වලින් කිවහොත් දැනටමත් ආරම්භ කර ඇති කාර්යයකි. RunSynchronously()උණුසුම් කාර්යයක් ඉල්ලා සිටීමෙන් ප්‍රති results ල අ InvalidOperationException. මෙම කේතය සමඟ එය උත්සාහ කරන්න:Task.Run(() => {}).RunSynchronously();
තියඩෝර් සූලියස්
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.