আমি কীভাবে অ্যাসিঙ্কটি নির্ধারণ করতে / ডেডলকগুলির জন্য অপেক্ষা করতে পারি?


24

আমি একটি নতুন কোডবেস নিয়ে কাজ করছি যা অ্যাসিঙ্ক / অপেক্ষা করার ভারী ব্যবহার করে। আমার দলের বেশিরভাগ লোকেরা এ্যাসএনসি / প্রতীক্ষায় মোটামুটি নতুন। আমরা সাধারণত মাইক্রোসফ্ট দ্বারা নির্দিষ্ট হিসাবে সেরা অভ্যাসগুলি ধরে রাখার ঝোঁক রাখি , তবে সাধারণত অ্যাসিঙ্ক কলটি প্রবাহিত করার জন্য আমাদের প্রসঙ্গটি প্রয়োজন এবং এমন লাইব্রেরিগুলির সাথে কাজ করি যা না করে ConfigureAwait(false)

এই সমস্ত জিনিস একত্রিত করুন এবং আমরা নিবন্ধে বর্ণিত অ্যাসিঙ্ক ডেডলকগুলিতে চলে যাই ... সাপ্তাহিক। ইউনিট পরীক্ষার সময় এগুলি প্রদর্শিত হয় না, কারণ আমাদের উপহাসিত ডেটা উত্সগুলি (সাধারণত এর মাধ্যমে Task.FromResult) অচলাবস্থাটি ট্রিগার করার পক্ষে যথেষ্ট নয়। তাই রানটাইম বা ইন্টিগ্রেশন পরীক্ষার সময় কিছু পরিষেবা কল কেবল মধ্যাহ্নভোজনে যায় এবং কখনই ফিরে আসে না। এটি সার্ভারগুলিকে মেরে ফেলে এবং সাধারণত জিনিসগুলিতে গোলমাল করে।

সমস্যাটি হ'ল ভুলটি কোথায় ঘটেছিল তা ট্র্যাকিংয়ের ক্ষেত্রে (সাধারণত কেবল সমস্ত পথ অ্যাসিঙ্ক করা হয় না) সাধারণত ম্যানুয়াল কোড পরিদর্শন জড়িত, যা সময় সাপেক্ষ এবং স্বয়ংক্রিয়-সক্ষম নয়।

অচলাবস্থার কারণ কী তা নির্ণয়ের আরও ভাল উপায় কী?


1
ভাল প্রশ্ন; আমি নিজেই এটা ভাবছি। আপনি কি এই লোকটির asyncনিবন্ধের সংকলনটি পড়েছেন ?
রবার্ট হার্ভে

@ রবার্টহারভে - সম্ভবত সব না, তবে আমি কিছু পড়েছি। আরও "এই দুই / তিনটি কাজ যেকোন জায়গায় করতে ভুলবেন না অন্যথায় আপনার কোড রানটাইমের সময় ভয়াবহ মৃত্যুবরণ করবে"।
টেলাস্টিন

আপনি কি async বাদ দেওয়ার জন্য বা এর ব্যবহারকে সবচেয়ে উপকারী পয়েন্টগুলিতে হ্রাস করার জন্য উন্মুক্ত? অ্যাসিঙ্ক আইও সমস্ত বা কিছুই নয়।
usr ডিরেক্টরির

1
আপনি যদি অচলাবস্থা পুনরুত্পাদন করতে পারেন, আপনি কি ব্লকিং কলটি দেখতে কেবল স্ট্যাক ট্রেসের দিকে নজর দিতে পারবেন না?
সোভিক

2
যদি সমস্যাটি "সমস্ত উপায়ে async হয় না", তবে এর অর্থ হ'ল অচলাবস্থার অর্ধেকটি একটি traditionalতিহ্যবাহী অচলাবস্থা এবং এটি সিঙ্ক্রোনাইজেশন কনটেক্সট থ্রেডের স্ট্যাক ট্রেসে দৃশ্যমান হওয়া উচিত।
সুইভ

উত্তর:


4

ঠিক আছে - নিম্নলিখিতগুলি আপনার পক্ষে কোনও উপকারে আসবে কিনা তা সম্পর্কে আমি নিশ্চিত নই, কারণ এমন একটি সমাধান বিকাশে আমি কিছু অনুমান করেছি যা আপনার ক্ষেত্রে সত্য হতে পারে বা নাও হতে পারে। হতে পারে আমার "সমাধান" খুব তাত্ত্বিক এবং কেবল কৃত্রিম উদাহরণগুলির জন্য কাজ করে - আমি নীচের স্টাফের বাইরে কোনও পরীক্ষা করিনি।
তদতিরিক্ত, আমি নিম্নলিখিতটি বাস্তব সমাধানের চেয়ে আরও কার্যকরভাবে দেখতে পাব তবে প্রতিক্রিয়াগুলির অভাব বিবেচনা করে আমি মনে করি এটি এখনও কোনওটির চেয়ে ভাল হতে পারে (আমি আপনার প্রশ্নটি সমাধানের জন্য অপেক্ষা করতে দেখলাম, তবে পোস্ট হওয়া দেখে না দেখে আমি খেলতে শুরু করেছি) সমস্যাটি নিয়ে প্রায়)।

তবে যথেষ্ট বলেছিলেন: আসুন আমরা বলি যে আমাদের কাছে একটি সাধারণ ডেটা পরিষেবা রয়েছে যা একটি পূর্ণসংখ্যা পুনরুদ্ধারে ব্যবহার করা যেতে পারে:

public interface IDataService
{
    Task<int> LoadMagicInteger();
}

একটি সাধারণ বাস্তবায়ন অ্যাসিক্রোনাস কোড ব্যবহার করে:

public sealed class CustomDataService
    : IDataService
{
    public async Task<int> LoadMagicInteger()
    {
        Console.WriteLine("LoadMagicInteger - 1");
        await Task.Delay(100);
        Console.WriteLine("LoadMagicInteger - 2");
        var result = 42;
        Console.WriteLine("LoadMagicInteger - 3");
        await Task.Delay(100);
        Console.WriteLine("LoadMagicInteger - 4");
        return result;
    }
}

এখন, একটি সমস্যা দেখা দিয়েছে, যদি আমরা এই শ্রেণীর দ্বারা বর্ণিত কোডটি "ভুলভাবে" ব্যবহার করি। ফলাফলের মতো করে আইনের পরিবর্তে Fooভুলভাবে অ্যাক্সেস করে:Task.ResultawaitBar

public sealed class ClassToTest
{
    private readonly IDataService _dataService;

    public ClassToTest(IDataService dataService)
    {
        this._dataService = dataService;
    }

    public async Task<int> Foo()
    {
        var result = this._dataService.LoadMagicInteger().Result;
        return result;
    }
    public async Task<int> Bar()
    {
        var result = await this._dataService.LoadMagicInteger();
        return result;
    }
}

আমাদের (আপনার) এখন যা দরকার তা হল একটি পরীক্ষা লেখার একটি উপায় যা কল করার সময় সফল হয় Barতবে কল করার সময় ব্যর্থ হয় Foo(কমপক্ষে যদি আমি প্রশ্নটি সঠিকভাবে বুঝতে পারি তবে ;-))।

আমি কোডটি বলতে দেব; আমি এখানে যা এলাম তা এখানে (ভিজ্যুয়াল স্টুডিও টেস্ট ব্যবহার করে, তবে এটি NUnit ব্যবহার করেও কাজ করা উচিত):

DataServiceMockব্যবহার TaskCompletionSource<T>। এটি আমাদের পরীক্ষার রানের একটি নির্ধারিত সময়ে ফলাফল নির্ধারণ করতে দেয় যা নিম্নলিখিত পরীক্ষার দিকে নিয়ে যায়। মনে রাখবেন যে আমরা একটি প্রতিনিধিকে টাস্ককম্পশনসোর্সটি পরীক্ষায় ফিরে পাস করতে ব্যবহার করছি। আপনি এটিকে পরীক্ষার আরম্ভের পদ্ধতিতে ব্যবহার করতে এবং বৈশিষ্ট্যগুলি ব্যবহার করতে পারেন।

TaskCompletionSource<int> tcs = null;
this._dataService.LoadMagicIntegerMock = t => tcs = t;

Task<int> task = null;
TaskTestHelper.AssertDoesNotBlock(() => task = this._instance.Foo());

tcs.TrySetResult(42);

var result = task.Result;
Assert.AreEqual(42, result);

this._end = true;

এখানে যা ঘটছে তা হ'ল আমরা প্রথমে যাচাই করেছিলাম যে আমরা অবরুদ্ধকরণ ছাড়াই পদ্ধতিটি ছেড়ে দিতে পারি (যদি কারও অ্যাক্সেস করা হয় তবে এটি কাজ করবে না Task.Result- এক্ষেত্রে আমরা পদ্ধতিটি ফিরে না আসা পর্যন্ত কাজের ফলাফল উপলব্ধ না করা হয় বলে আমরা একটি সময়সীমার মধ্যে চলে যাব )।
তারপরে, আমরা ফলাফলটি নির্ধারণ করি (এখন পদ্ধতিটি কার্যকর করতে পারে) এবং আমরা ফলাফলটি যাচাই করি (ইউনিট পরীক্ষার অভ্যন্তরে আমরা টাস্ক অ্যাক্সেস করতে পারি ult ফলাফল হিসাবে আমরা বাস্তবে অবরুদ্ধ হওয়া চাই ) es

সম্পূর্ণ পরীক্ষার শ্রেণি - পছন্দ হিসাবে BarTestসাফল্য ও FooTestব্যর্থতা।

[TestClass]
public class UnitTest1
{
    private DataServiceMock _dataService;
    private ClassToTest _instance;
    private bool _end;

    [TestInitialize]
    public void Initialize()
    {
        this._dataService = new DataServiceMock();
        this._instance = new ClassToTest(this._dataService);

        this._end = false;
    }
    [TestCleanup]
    public void Cleanup()
    {
        Assert.IsTrue(this._end);
    }

    [TestMethod]
    public void FooTest()
    {
        TaskCompletionSource<int> tcs = null;
        this._dataService.LoadMagicIntegerMock = t => tcs = t;

        Task<int> task = null;
        TaskTestHelper.AssertDoesNotBlock(() => task = this._instance.Foo());

        tcs.TrySetResult(42);

        var result = task.Result;
        Assert.AreEqual(42, result);

        this._end = true;
    }
    [TestMethod]
    public void BarTest()
    {
        TaskCompletionSource<int> tcs = null;
        this._dataService.LoadMagicIntegerMock = t => tcs = t;

        Task<int> task = null;
        TaskTestHelper.AssertDoesNotBlock(() => task = this._instance.Bar());

        tcs.TrySetResult(42);

        var result = task.Result;
        Assert.AreEqual(42, result);

        this._end = true;
    }
}

ডেডলকস / সময়সীমা পরীক্ষা করার জন্য একটি সামান্য সহায়ক শ্রেণি:

public static class TaskTestHelper
{
    public static void AssertDoesNotBlock(Action action, int timeout = 1000)
    {
        var timeoutTask = Task.Delay(timeout);
        var task = Task.Factory.StartNew(action);

        Task.WaitAny(timeoutTask, task);

        Assert.IsTrue(task.IsCompleted);
    }
}

চমৎকার উত্তর. আমি যখন কিছুটা সময় পেলাম তখন নিজেই আপনার কোডটি চেষ্টা করার পরিকল্পনা করছিলাম (এটি আসলে কাজ করে কি না তা আমি নিশ্চিতভাবে জানি না), তবে এই প্রচেষ্টার জন্য কুডোস এবং একটি উর্ধ্বতন।
রবার্ট হার্ভে

-2

এখানে একটি কৌশল যা আমি একটি বিশাল এবং খুব, খুব বহুতলযুক্ত অ্যাপ্লিকেশনটিতে ব্যবহার করেছি:

প্রথমত, আপনাকে একটি মিটেক্সের চারপাশে কিছু ডেটা কাঠামো প্রয়োজন (দুর্ভাগ্যক্রমে) এবং কোনও সিঙ্ক্রোনাইজিং কল ডিরেক্টরি তৈরি করবেন না। সেই ডেটা স্ট্রাকচারে পূর্বের কোনও লক করা মিটেক্সের লিঙ্ক রয়েছে। প্রতিটি মিউটেক্সের 0 থেকে শুরু করে একটি "স্তর" থাকে, যা আপনি নির্ধারণ করেন যখন মিটেক্স তৈরি হবে এবং কখনই পরিবর্তন হতে পারে না।

এবং নিয়মটি হ'ল: যদি কোনও মিউটেক্স লক করা থাকে তবে আপনাকে কেবল অন্য স্তরের স্তনকে কেবল সর্বদা লক করতে হবে। যদি আপনি এই নিয়মটি অনুসরণ করেন তবে আপনার অচল তাল থাকতে পারে না। যখন আপনি কোনও লঙ্ঘন খুঁজে পান, আপনার আবেদন এখনও ঠিক আছে এবং ঠিক চলছে।

যখন আপনি কোনও লঙ্ঘন খুঁজে পান, তখন দুটি সম্ভাবনা থাকে: আপনি স্তরগুলি ভুল হিসাবে নির্ধারণ করতে পারেন। আপনি A কে লকিংয়ের পরে B কে লক করেছেন, সুতরাং B এর নিম্ন স্তরটি হওয়া উচিত ছিল। সুতরাং আপনি স্তরটি স্থির করে আবার চেষ্টা করুন।

অন্য সম্ভাবনা: আপনি এটি ঠিক করতে পারবেন না। আপনার লকগুলির কোডগুলি এ এর ​​পরে বি লক করে, অন্য কিছু কোড বি লক করে এটিকে লক করে এটিকে অনুমতি দেওয়ার জন্য স্তরগুলি নির্ধারণের কোনও উপায় নেই। এবং অবশ্যই এটি একটি সম্ভাব্য অচলাবস্থা: উভয় কোড যদি বিভিন্ন থ্রেডে একযোগে চলতে থাকে তবে অচলাবস্থার সম্ভাবনা রয়েছে।

এটি প্রবর্তনের পরে, একটি সংক্ষিপ্ত পর্যায়ে ছিল যেখানে স্তরগুলি সামঞ্জস্য করতে হয়েছিল, তারপরে একটি দীর্ঘ পর্বের পরে সম্ভাব্য অচলাবস্থা পাওয়া গেছে।


4
আমি দুঃখিত, এটি কীভাবে async / আচরণের অপেক্ষায় প্রযোজ্য? আমি কার্যত সমান্তরাল লাইব্রেরিতে কাস্টম মিটেক্স পরিচালনা কাঠামোটি বাস্তবে ইনজেক্ট করতে পারি না।
টেলাস্টিন

-3

আপনি কি অ্যাসিঙ্ক / অ্যাওয়েট ব্যবহার করছেন যাতে আপনি কোনও ডাটাবেসের মতো ব্যয়বহুল কলগুলির সমান্তরাল করতে পারেন? ডিবিতে মৃত্যুদন্ড কার্যকর করার পথে এটি সম্ভব নাও হতে পারে।

অ্যাসিঙ্ক / প্রতীক্ষার সাথে পরীক্ষার কভারেজ চ্যালেঞ্জিং হতে পারে এবং বাগগুলি খুঁজে পাওয়ার জন্য আসল উত্পাদন ব্যবহারের মতো কিছুই নেই। আপনি যে প্যাটার্নটিকে বিবেচনা করতে পারেন তা হ'ল একটি সম্পর্কিত আইডি পাস করা এবং এটি স্ট্যাকের নিচে লগ ইন করা, তারপরে একটি ক্যাসকেডিং টাইমআউট থাকে যা ত্রুটিটি লগ করে। এটি একটি এসওএ প্যাটার্নের বেশি তবে কমপক্ষে এটি আপনাকে বোঝা দেবে যে এটি কোথা থেকে আসছে। ডেডলকগুলি খুঁজে পেতে আমরা এটি স্প্লঙ্কের সাথে ব্যবহার করেছি।

আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.