مشاركة عبر


الإرشادات التفصيلية: تصحيح الأخطاء لتطبيق متوازي

توضح هذه المعاينة كيفية استخدام النوافذ المهام المتوازية و المكدسات المتوازية لتصحيح تطبيق متوازي, حيث تقوم هذه النوافذ بمساعدتك على الفهم والتحقق من التعليمات البرمجية للسلوك وقت التشغيل التي تستخدم نظرة عامة مكتبة Parallel المهمة أو وقت تشغيل التزامن, و تقوم هذه المعاينة بتوفير نموذج التعليمات البرمجية التي تحتوي على نقاط التوقف المدمجة, فبعد فصل التعليمات البرمجية تقوم المعاينة بعرض كيفية استخدام النوافذ المهام المتوازية و المكدسات المتوازية لفحصه.

تقوم هذه المعاينة بتعليم تلك المهام ما يلي:

  • كيفية عرض مكدسات الاستدعاءات لكافة مؤشرات الترابط في عرض واحد.

  • كيفية عرض القائمة للحالات System.Threading.Tasks.Task التي تم إنشاؤها في التطبيق الخاص بك.

  • كيفية عرض مكدسات الاستدعاءات الحقيقية للمهام بدلاً من مؤشرات الترابط.

  • كيفية الانتقال إلى التعليمات البرمجية من النوافذ المهام المتوازية و المكدسات المتوازية.

  • كيفية نوافذ cope مع تغيير الحجم من خلال التجميع و التكبير/التصغير و الميزات الأخرى ذات الصلة.

المتطلبات الأساسية

يجب أن يكون لديك Visual Studio 2010 مثبتاً على الكمبيوتر.

تقوم هذه المعاينة بافتراض أن التعليمات البرمجية الخاصة بي فقط ممكنة, فعلى القائمة أدوات, انقر فوق خيارات ثم قم بتوسيع العقدة تصحيح الأخطاء ومن ثم حدد عام ثم قم بتحديد تمكين التعليمات البرمجية الخاصة بي فقط(المدارة فقط), فإذا لم تقم بتعيين هذه الميزة فلا يزال بإمكانك استخدام هذه المعاينة و لكن قد تختلف النتائج عن الرسومات التوضيحية.

نموذج C#‎

إذا كنت تستخدم نموذج C#, فإن هذه المعاينة تفترض أيضاً أن التعليمات البرمجية الخارجية مخفية, و لمعرفة ما إذا كانت التعليمات البرمجية الخارجية معروضة, انقر بزر الماوس الأيمن فوق عنوان الجدول اسم للنافذة مكدس الاستدعاءات, ثم قم بتحديد أو إلغاء إظهار التعليمات البرمجية الخارجية, فإذا لم تقم بتعيين هذه الميزة فلا يزال بإمكانك استخدام هذه المعاينة و لكن قد تختلف النتائج عن الرسومات التوضيحية.

نموذج C#‎

يمكن تجاهل الإشارة إلى التعليمات البرمجية الخارجية في هذا الموضوع إذا قمت باستخدام نموذج لـ C++ , و فقط التعليمات البرمجية الخارجية تنطبق على نموذج C#‎.

الرسومات التوضيحية

إن الرسومات التوضيحية في هذا الموضوع سجلت على أساس معرّف رباعي للكمبيوتر يقوم بتشغيل نموذج C#, و على الرغم من تمكنك من استخدام تكوينات أخرى لإتمام هذه المعاينة فإن الرسومات التوضيحية قد تختلف عن ما يتم عرضه على جهاز الكمبيوتر الخاص بك.

إنشاء نموذج المشروع

إن نموذج التعليمات البرمجية في هذه المعاينة هو لتطبيق ما لا وظيفة له, و إن الهدف من ذلك هو فهم كيفية استخدام نوافذ الأدوات لتصحيح تطبيق متوازي.

لإنشاء نموذج المشروع

  1. في ‏‫Visual Studio , على القائمة ملف قم بالإشارة إلى جديد ثم انقر فوق مشروع.

  2. في الجزء القوالب المثبتة, قم بتحديد إما Visual C# أو Visual Basic أو Visual C++. للغات المدارة, تأكد من أن .NET Framework 4 تم عرضها في مربع إطار العمل.

  3. حدد تطبيق وحدة التحكم ثم انقر فوق ‏‏موافق حيث يبقى في تكوين التصحيح الافتراضي.

  4. افتح ملف التعليمات البرمجية .cpp أو .cs أو .vb في المشروع ثم قم بحذف محتوياته لإنشاء ملف تعليمات برمجية فارغ.

  5. إلصق التعليمات البرمجية التالية للغتك المختارة في ملف التعليمات البرمجية الفارغ.

Imports System
Imports System.Threading
Imports System.Threading.Tasks
Imports System.Diagnostics

Module S

  Sub Main()

    pcount = Environment.ProcessorCount
    Console.WriteLine("Proc count = " + pcount.ToString())
    ThreadPool.SetMinThreads(4, -1)
    ThreadPool.SetMaxThreads(4, -1)

    t1 = New Task(AddressOf A, 1)
    t2 = New Task(AddressOf A, 2)
    t3 = New Task(AddressOf A, 3)
    t4 = New Task(AddressOf A, 4)
    Console.WriteLine("Starting t1 " + t1.Id.ToString())
    t1.Start()
    Console.WriteLine("Starting t2 " + t2.Id.ToString())
    t2.Start()
    Console.WriteLine("Starting t3 " + t3.Id.ToString())
    t3.Start()
    Console.WriteLine("Starting t4 " + t4.Id.ToString())
    t4.Start()

    Console.ReadLine()
  End Sub
  Sub A(ByVal o As Object)
    B(o)
  End Sub
  Sub B(ByVal o As Object)
    C(o)
  End Sub
  Sub C(ByVal o As Object)

    Dim temp As Integer = o

    Interlocked.Increment(aa)
    While (aa < 4)
    End While

    If (temp = 1) Then
      ' BP1 - all tasks in C
      Debugger.Break()
      waitFor1 = False
    Else
      While (waitFor1)
      End While
    End If
    Select Case temp
      Case 1
        D(o)
      Case 2
        F(o)
      Case 3, 4
        I(o)
      Case Else
        Debug.Assert(False, "fool")
    End Select
  End Sub
  Sub D(ByVal o As Object)
    E(o)
  End Sub
  Sub E(ByVal o As Object)
    ' break here at the same time as H and K
    While (bb < 2)
    End While
    'BP2 - 1 in E, 2 in H, 3 in J, 4 in K
    Debugger.Break()
    Interlocked.Increment(bb)

    'after
    L(o)
  End Sub
  Sub F(ByVal o As Object)
    G(o)
  End Sub
  Sub G(ByVal o As Object)
    H(o)
  End Sub
  Sub H(ByVal o As Object)
    ' break here at the same time as E and K
    Interlocked.Increment(bb)
    Monitor.Enter(mylock)
    While (bb < 3)
    End While
    Monitor.Exit(mylock)

    'after
    L(o)
  End Sub
  Sub I(ByVal o As Object)
    J(o)
  End Sub
  Sub J(ByVal o As Object)

    Dim temp2 As Integer = o

    Select Case temp2
      Case 3
        t4.Wait()
      Case 4
        K(o)
      Case Else
        Debug.Assert(False, "fool2")
    End Select
  End Sub
  Sub K(ByVal o As Object)
    ' break here at the same time as E and H
    Interlocked.Increment(bb)
    Monitor.Enter(mylock)
    While (bb < 3)
    End While
    Monitor.Exit(mylock)

    'after
    L(o)
  End Sub
  Sub L(ByVal oo As Object)
    Dim temp3 As Integer = oo

    Select Case temp3
      Case 1
        M(oo)
      Case 2
        N(oo)
      Case 4
        O(oo)
      Case Else
        Debug.Assert(False, "fool3")
    End Select
  End Sub
  Sub M(ByVal o As Object)
    ' breaks here at the same time as N and Q
    Interlocked.Increment(cc)
    While (cc < 3)
    End While

    'BP3 - 1 in M, 2 in N, 3 still in J, 4 in O, 5 in Q
    Debugger.Break()
    Interlocked.Increment(cc)
    While (True)
      Thread.Sleep(500) '  for ever
    End While
  End Sub
  Sub N(ByVal o As Object)
    ' breaks here at the same time as M and Q
    Interlocked.Increment(cc)
    While (cc < 4)
    End While
    R(o)
  End Sub
  Sub O(ByVal o As Object)
    Dim t5 As Task = Task.Factory.StartNew(AddressOf P, TaskCreationOptions.AttachedToParent)
    t5.Wait()
    R(o)
  End Sub
  Sub P()
    Console.WriteLine("t5 runs " + Task.CurrentId.ToString())
    Q()
  End Sub
  Sub Q()
    ' breaks here at the same time as N and M
    Interlocked.Increment(cc)
    While (cc < 4)
    End While
    ' task 5 dies here freeing task 4 (its parent)
    Console.WriteLine("t5 dies " + Task.CurrentId.ToString())
    waitFor5 = False
  End Sub
  Sub R(ByVal o As Object)
    If (o = 2) Then
      ' wait for task5 to die
      While waitFor5
      End While

      '//spin up all procs
      Dim i As Integer
      For i = 0 To pcount - 4 - 1

        Dim t As Task = Task.Factory.StartNew(Sub()
                                                While True

                                                End While
                                              End Sub)
        Console.WriteLine("Started task " + t.Id.ToString())
      Next

      Task.Factory.StartNew(AddressOf T, i + 1 + 5, TaskCreationOptions.AttachedToParent) ' //scheduled
      Task.Factory.StartNew(AddressOf T, i + 2 + 5, TaskCreationOptions.AttachedToParent) ' //scheduled
      Task.Factory.StartNew(AddressOf T, i + 3 + 5, TaskCreationOptions.AttachedToParent) ' //scheduled
      Task.Factory.StartNew(AddressOf T, i + 4 + 5, TaskCreationOptions.AttachedToParent) ' //scheduled
      Task.Factory.StartNew(AddressOf T, (i + 5 + 5).ToString(), TaskCreationOptions.AttachedToParent) ' //scheduled

      '//BP4 - 1 in M, 2 in R, 3 in J, 4 in R, 5 died
      Debugger.Break()

    Else
      Debug.Assert(o = 4)
      t3.Wait()
    End If
  End Sub
  Sub T(ByVal o As Object)
    Console.WriteLine("Scheduled run " + Task.CurrentId.ToString())
  End Sub
  Private t1, t2, t3, t4 As Task
  Private aa As Integer = 0
  Private bb As Integer = 0
  Private cc As Integer = 0
  Private waitFor1 As Boolean = True
  Private waitFor5 As Boolean = True
  Private pcount As Integer
  Private mylock As New S2()
End Module

Public Class S2

End Class
using System;
using System.Threading;
using System.Threading.Tasks;
using System.Diagnostics;

class S
{
  static void Main()
  {
    pcount = Environment.ProcessorCount;
    Console.WriteLine("Proc count = " + pcount);
    ThreadPool.SetMinThreads(4, -1);
    ThreadPool.SetMaxThreads(4, -1);

    t1 = new Task(A, 1);
    t2 = new Task(A, 2);
    t3 = new Task(A, 3);
    t4 = new Task(A, 4);
    Console.WriteLine("Starting t1 " + t1.Id.ToString());
    t1.Start();
    Console.WriteLine("Starting t2 " + t2.Id.ToString());
    t2.Start();
    Console.WriteLine("Starting t3 " + t3.Id.ToString());
    t3.Start();
    Console.WriteLine("Starting t4 " + t4.Id.ToString());
    t4.Start();

    Console.ReadLine();
  }

  static void A(object o)
  {
    B(o);
  }
  static void B(object o)
  {
    C(o);
  }
  static void C(object o)
  {
    int temp = (int)o;

    Interlocked.Increment(ref aa);
    while (aa < 4)
    {
      ;
    }

    if (temp == 1)
    {
      // BP1 - all tasks in C
      Debugger.Break();
      waitFor1 = false;
    }
    else
    {
      while (waitFor1)
      {
        ;
      }
    }
    switch (temp)
    {
      case 1:
        D(o);
        break;
      case 2:
        F(o);
        break;
      case 3:
      case 4:
        I(o);
        break;
      default:
        Debug.Assert(false, "fool");
        break;
    }
  }
  static void D(object o)
  {
    E(o);
  }
  static void E(object o)
  {
    // break here at the same time as H and K
    while (bb < 2)
    {
      ;
    }
    //BP2 - 1 in E, 2 in H, 3 in J, 4 in K
    Debugger.Break();
    Interlocked.Increment(ref bb);

    //after
    L(o);
  }
  static void F(object o)
  {
    G(o);
  }
  static void G(object o)
  {
    H(o);
  }
  static void H(object o)
  {
    // break here at the same time as E and K
    Interlocked.Increment(ref bb);
    Monitor.Enter(mylock);
    while (bb < 3)
    {
      ;
    }
    Monitor.Exit(mylock);


    //after
    L(o);
  }
  static void I(object o)
  {
    J(o);
  }
  static void J(object o)
  {
    int temp2 = (int)o;

    switch (temp2)
    {
      case 3:
        t4.Wait();
        break;
      case 4:
        K(o);
        break;
      default:
        Debug.Assert(false, "fool2");
        break;
    }
  }
  static void K(object o)
  {
    // break here at the same time as E and H
    Interlocked.Increment(ref bb);
    Monitor.Enter(mylock);
    while (bb < 3)
    {
      ;
    }
    Monitor.Exit(mylock);


    //after
    L(o);
  }
  static void L(object oo)
  {
    int temp3 = (int)oo;

    switch (temp3)
    {
      case 1:
        M(oo);
        break;
      case 2:
        N(oo);
        break;
      case 4:
        O(oo);
        break;
      default:
        Debug.Assert(false, "fool3");
        break;
    }
  }
  static void M(object o)
  {
    // breaks here at the same time as N and Q
    Interlocked.Increment(ref cc);
    while (cc < 3)
    {
      ;
    }
    //BP3 - 1 in M, 2 in N, 3 still in J, 4 in O, 5 in Q
    Debugger.Break();
    Interlocked.Increment(ref cc);
    while (true)
      Thread.Sleep(500); // for ever
  }
  static void N(object o)
  {
    // breaks here at the same time as M and Q
    Interlocked.Increment(ref cc);
    while (cc < 4)
    {
      ;
    }
    R(o);
  }
  static void O(object o)
  {
    Task t5 = Task.Factory.StartNew(P, TaskCreationOptions.AttachedToParent);
    t5.Wait();
    R(o);
  }
  static void P()
  {
    Console.WriteLine("t5 runs " + Task.CurrentId.ToString());
    Q();
  }
  static void Q()
  {
    // breaks here at the same time as N and M
    Interlocked.Increment(ref cc);
    while (cc < 4)
    {
      ;
    }
    // task 5 dies here freeing task 4 (its parent)
    Console.WriteLine("t5 dies " + Task.CurrentId.ToString());
    waitFor5 = false;
  }
  static void R(object o)
  {
    if ((int)o == 2)
    {
      //wait for task5 to die
      while (waitFor5) { ;}


      int i;
      //spin up all procs
      for (i = 0; i < pcount - 4; i++)
      {
        Task t = Task.Factory.StartNew(() => { while (true);});
        Console.WriteLine("Started task " + t.Id.ToString());
      }

      Task.Factory.StartNew(T, i + 1 + 5, TaskCreationOptions.AttachedToParent); //scheduled
      Task.Factory.StartNew(T, i + 2 + 5, TaskCreationOptions.AttachedToParent); //scheduled
      Task.Factory.StartNew(T, i + 3 + 5, TaskCreationOptions.AttachedToParent); //scheduled
      Task.Factory.StartNew(T, i + 4 + 5, TaskCreationOptions.AttachedToParent); //scheduled
      Task.Factory.StartNew(T, (i + 5 + 5).ToString(), TaskCreationOptions.AttachedToParent); //scheduled

      //BP4 - 1 in M, 2 in R, 3 in J, 4 in R, 5 died
      Debugger.Break();
    }
    else
    {
      Debug.Assert((int)o == 4);
      t3.Wait();
    }
  }
  static void T(object o)
  {
    Console.WriteLine("Scheduled run " + Task.CurrentId.ToString());
  }
  static Task t1, t2, t3, t4;
  static int aa = 0;
  static int bb = 0;
  static int cc = 0;
  static bool waitFor1 = true;
  static bool waitFor5 = true;
  static int pcount;
  static S mylock = new S();
}
#include "stdafx.h"
#include <windows.h>
#include <iostream>
#include <ppl.h>
#include <agents.h>
#include <stdio.h>
#include <concrtrm.h>
#include <vector>

CRITICAL_SECTION cs;

using namespace ::std;
using namespace ::std::tr1;
using namespace ::Concurrency;
task_group task4;
task_group task3;
task_group task2;

volatile long aa = 0;
volatile long bb = 0;
volatile long cc = 0;
static bool waitFor1 = true;
static bool waitFor5 = true;

#pragma optimize("", off)
void Spin()
{
    for(int i=0;i<50*50000;++i);
}
#pragma optimize("",on)

template<class Func>
class RunFunc
{
    Func& m_Func;
    int m_o;
public:
    RunFunc(Func func,int o):m_Func(func),m_o(o){

    };
    void operator()()const{
        m_Func(m_o);
    };
};

void T(int o)
{
    cout << "Scheduled run \n";

};

void R(int o)
{
    if (o == 2)
    {
        while (waitFor5) { ;}
        Spin();

        //use up all processors but 4 by scheduling 4 non-terminating tasks.
        int numProcsToBurn = GetProcessorCount() - 4;
        int i;
        vector<call<int>*> tasks;
        for (i = 0; i <  numProcsToBurn; i++)
        {
            tasks.push_back(new call<int>([](int i){while(true)Spin();}));
            asend(tasks[i],1);
            cout << "Started task  \n";
        }

        task_handle<RunFunc<decltype(T)>> t6(RunFunc<decltype(T)>(T,i + 1 + 5));
        task_handle<RunFunc<decltype(T)>> t7(RunFunc<decltype(T)>(T,i + 2 + 5));
        task_handle<RunFunc<decltype(T)>> t8(RunFunc<decltype(T)>(T,i + 3 + 5));
        task_handle<RunFunc<decltype(T)>> t9(RunFunc<decltype(T)>(T,i + 4 + 5));
        task_handle<RunFunc<decltype(T)>> t10(RunFunc<decltype(T)>(T,i + 5 + 5));
        task2.run(t6);
        task2.run(t7);
        task2.run(t8);
        task2.run(t9);
        task2.run(t10);
    
        //BP4 - 1 in M, 2 in R, 3 in J, 4 in R, 5 died  
        DebugBreak();
    }
    else
    {
        if (o!=4)
            throw;

        task3.wait();       
    }
};

void Q()
{
    // breaks here at the same time as N and M
    InterlockedIncrement(& cc);
    while (cc < 4)
    {
        ;
    }
    // task 5 dies here freeing task 4 (its parent)
    cout << "t5 dies\n";
    waitFor5 = false;
};

void P()
{
    cout << "t5 runs\n";
    Q();
};

void O(int o)
{   
    task_group t5;
    t5.run(&P);
    t5.wait();
    R(o);
};

void N(int o)
{
    // breaks here at the same time as M and Q
    InterlockedIncrement(&cc);
    while (cc < 4)
    {
        ;
    }
    R(o);
};

void M(int o)
{
    // breaks here at the same time as N and Q
    InterlockedIncrement(&cc);
    while (cc < 3)
    {
        ;
    }
    //BP3 - 1 in M, 2 in N, 3 still in J, 4 in O, 5 in Q
    DebugBreak();
    InterlockedIncrement(&cc);
    while (true)
        Sleep(500); // for ever
};

void L(int oo)
{
    int temp3 = oo;

    switch (temp3)
    {
    case 1:
        M(oo);
        break;
    case 2:
        N(oo);
        break;
    case 4:
        O(oo);
        break;
    default:
        throw; //fool3
        break;
    }
}
void K(int o)
{
    // break here at the same time as E and H
    InterlockedIncrement(&bb);
    EnterCriticalSection(&cs);
    while (bb < 3)
    {
        ;
    }
    LeaveCriticalSection(&cs);
    Spin();

    //after
    L(o);
}
void J(int o)
{
    int temp2 = o;

    switch (temp2)
    {
    case 3:
        task4.wait();
        break;
    case 4:
        K(o);
        break;
    default:
        throw; //fool2
        break;
    }
}
static void I(int o)
{
    J(o);
}
static void H(int o)
{
    // break here at the same time as E and K
    InterlockedIncrement(&bb);
    EnterCriticalSection(&cs);
    while (bb < 3)
    {
        ;
    }
    LeaveCriticalSection(&cs);
    Spin();

    //after
    L(o);
}
static void G(int o)
{
    H(o);
}
static void F(int o)
{
    G(o);
}

static void E(int o)
{
    // break here at the same time as H and K
    while (bb < 2)
    {
        ;
    }
    //BP2 - 1 in E, 2 in H, 3 in J, 4 in K  
    Spin(); // for native case only
    DebugBreak();
    InterlockedIncrement(&bb);

    //after
    L(o);

}

static void D(int o)
{
    E(o);
}

static void C(int o)
{
    int temp = o;

    InterlockedIncrement(&aa);
    while (aa < 4)
    {
        ;
    }

    if (temp == 1)
    {
        // BP1 - all tasks in C 
        DebugBreak();
        waitFor1 = false;
    }
    else
    {
        while (waitFor1)
        {
            ;
        }
    }
    switch (temp)
    {
    case 1:
        D(o);
        break;
    case 2:
        F(o);
        break;
    case 3:
    case 4:
        I(o);
        break;
    default:
        throw; //fool
        break;
    }
}
static void B(int o)
{
    C(o);
}

void A(int o)
{
    B(o);
}
int main()
{
    InitializeCriticalSection(&cs);

    task_group tasks;
    task_handle<RunFunc<decltype(A)>> t1(RunFunc<decltype(A)>(A,1));
    tasks.run(t1);
    task_handle<RunFunc<decltype(A)>> t2(RunFunc<decltype(A)>(A,2));
    task2.run(t2);
    task_handle<RunFunc<decltype(A)>> t3(RunFunc<decltype(A)>(A,3));
    task3.run(t3);
    task_handle<RunFunc<decltype(A)>> t4(RunFunc<decltype(A)>(A,4));
    task4.run(t4);
    
    getchar();
    return 1;
}
  1. من القائمة ملف، انقر فوق حفظ الكل.

  2. من القائمة Build (إنشاء)، انقر فوق Rebuild Solution ( إعادة إنشاء الحل).

    لاحظ أن هناك أربع استدعاءات Debugger.Break (DebugBreak في نموذج C++), فقط تشغيل التطبيق سوف يؤدي إلى فصل في المصحح أكثر من أربع مرات, فبالتالي ليس عليك إدراج نقاط التوقف.

باستخدام نافذة المكدسات المتوازية: عرض مؤشرات الترابط

في قائمة Debug ، قم بالنقر فوق Start Debugging. انتظر حتى يتم الدخول إلى نقطة التوقف الأولى.

لعرض مكدسات الاستدعاءات لمؤشر ترابط مفرد

  1. على القائمة تصحيح, قم بالتأشير على نوافذ ثم انقر فوق مؤشرات الترابط. إرساء النافذة مؤشرات الترابط في الجزء السفلي من ‏‫Visual Studio.

  2. على القائمة تصحيح, قم بالإشارة إلى نوافذ ثم انقر فوق مكدسات الاستدعاءات. إرساء النافذة مكدسات الاستدعاءات في الجزء السفلي من ‏‫Visual Studio.

  3. قم بالنقر المزدوج على النافذة مؤشرات الترابط لتحديثها, فمؤشرات الترابط الحالية لديها سهم أصفر, و عندما تقوم بتغيير مؤشر الترابط الحالي, فإن مكدس الاستدعاء الخاص به يتم عرضه في النافذة مكدس الاستدعاءات.

لفحص نافذة المكدسات المتوازية

  • على القائمة تصحيح, قم بالإشارة إلى نوافذ ثم انقر فوق المكدسات المتوازية ثم تأكد من أن مؤشرات الترابط تم تحديدها في المربع الموجود في الزاوية العلوية اليمنى.

    باستخدام النافذة المكدسات المتوازية يمكنك عرض مكدسات الاستدعاءات المتعددة في نفس الوقت بطريقة عرض واحدة. يبين الرسم التوضيحي التالي النافذة المكدسات المتوازية أعلى النافذة مكدس الاستدعاءات.

    نافذة المكدسات المتوازية في طريقة عرض مؤشرات الترابط

    يتم إظهار مكدس الاستدعاءات لمؤشر الترابط الرئيسي في مربع واحد و يتم تجميع مكدسات الاستدعاءات لمؤشرات الترابط الأربع الأخرى في مربع آخر, حيث تم تجميع مؤشرات الترابط الأربعة معاً لأن إطارات المكدس الخاصة بهم مشتركة بسياقات الأسلوب نفسها و هذا يعني أنها موجودة في الأساليب نفسها: A ، B و C و لعرض معرفات مؤشر الترابط و أسماء مؤشرات الترابط التي تشترك في نفس المربع, قم بالمرور فوق العنوان (مؤشرات الترابط). يتم عرض مؤشر الترابط الحالي في غامق, كما هو مبين في الرسم التوضيحي التالي.

    تلميح أداة به معرفات مؤشرات الترابط والأسماء

    إن السهم الأصفر يشير إلى إطار المكدس النشط لمؤشر الترابط الحالي. للحصول على مزيد من المعلومات, قم بالمرور فوقها

    تليمح أداة على إطار مكدس نشط

    يمكنك تعيين مقدار التفاصيل المطلوب إظهارها لإطارات المكدس (أسماء الوحدة النمطية و أنواع المعلمة و أسماء المعلمة و قيم المعلمة و أرقام الأسطر و إزاحات البايت) عن طريق النقر بزر الماوس الأيمن فوق النافذة مكدس الاستدعاءات.

    يشير تمييز أزرق حول مربع إلى أن مؤشر الترابط الحالي هو جزء من هذا المربع, و يتم أيضاً الإشارة إلى مؤشر الترابط الحالي بواسطة إطار المكدس الغامق في تلميح الأدوات. إذا قمت بالنقر المزدوج على مؤشر الترابط الرئيسي في نافذة مؤشرات الترابط, فيمكنك الانتباه إلى أن التمييز الأزرق في النافذة المكدسات المتوازية تتحرك بناءاً على ذلك.

    مكدسات بمؤشر ترابط رئيسي مميز باللون الأزرق

لاستئناف التنفيذ حتى نقطة التوقف الثانية

  • لاستئناف التنفيذ حتى يتم إدخال نقطة التوقف الثانية على القائمة تصحيح, انقر فوق متابعة. يبين الرسم التوضيحي التالي شجرة مؤشر الترابط عند نقطة التوقف الثانية.

    نافذة المكدسات المتوازية بها عدة أقسام

    عند نقطة التوقف الأولى، فإن أربعة مؤشرات ترابط ذهبت من الأساليب S.A إلى S.B و إلى S.C و هذه المعلومات ما زالت مرئية في النافذة المكدسات المتوازية, لكن مؤشرات الترابط الأربعة حققت تقدماً بشكل أكبر, فأحدها واصل إلى S.D ثم S.E والأخرى واصلت إلى S.F و S.G و S.H و الأخرتين واصلتا إلى S.I ثم S.J و من هناك إحداهما ذهبت إلى S.K و الأخرى واصلت إلى تعليمات برمجية لغير-مستخدم.

    يمكنك المرور فوق عنوان المربع, على سبيل المثال، مؤشر ترابط واحد أو مؤشرات ترابط ، لرؤية معرفات مؤشر الترابط لمؤشرات الترابط, و يمكن المرور فوق إطارات المكدس لرؤية معرفات مؤشر الترابط بالإضافة إلى تفاصيل إطار آخر, فالتمييز الأزرق يشير إلى مؤشر الترابط الحالي أما السهم الأصفر يشير إلى إطار المكدس النشط لمؤشر الترابط الحالي

    تشير أيقونة قطعة مؤشرات الترابط (تداخل خطوط متموجة زرقاء و حمراء) إلى إطارات المكدس النشطة لمؤشرات الترابط الغير حالية, ففي النافذة مكدس الاستدعاءات قم بالنقر المزدوج فوق S.B لتبديل الإطارات, و النافذة المكدسات المتوازية تشير إلى إطار المكدس الحالي لمؤشرات الترابط الحالية باستخدام أيقونة سهم خضراء منحنية.

    في النافذة مؤشرات الترابط، قم بالتبديل بين مؤشرات الترابط ولاحظ أن العرض في النافذة المكدسات المتوازية تم تحديثه.

    يمكنك التبديل إلى مؤشر الترابط الآخر أو إلى الإطار الآخر لمؤشر الترابط باستخدام القائمة المختصرة في النافذة المكدسات المتوازية. على سبيل المثال، انقر بزر الماوس الأيمن على S.J ، قم بالإشارة إلى التبديل إلى الإطار ، ثم انقر فوق أحد الأوامر.

    مسار تنفيذ المكدسات المتوازية

    انقر بزر الماوس الأيمن على S.C ثم قم بالإشارة على التبديل إلى الإطار, و أحد الأوامر له علامة يشير إلى إطار المكدسات لمؤشر الترابط الحالي فيمكنك التبديل إلى ذلك الإطار لمؤشر الترابط نفسه (سيتم نقل السهم الأخضر فقط) أو يمكنك يمكن التبديل إلى مؤشر الترابط الآخر (سيتم نقل التمييز الأزرق أيضاً). يوضح الرسم التوضيحي التالي القائمة الفرعية.

    قائمة المكدسات وبها خياران على C في وجود J

    عندما يتم إقتران سياق أسلوب مع إطار مكدس واحد فقط, فإن عنوان المربع يعرض مؤشر الترابط و يمكنك التبديل إليه بنقره نقراً مزدوجاً, و إذا قمت بالنقر المزدوج فوق سياق أسلوب يحتوي على على أكثر من إطار, فإن القائمة تنبثق تلقائياً كأنك تقوم بالمرور فوق سياقات الأسلوب, لاحظ المثلث الأسود على اليمين, و يؤدي النقر فوق هذا المثلث بعرض القائمة المختصرة.

    قد ترغب في التركيز على مجموعة فرعية من مؤشرات الترابط للتطبيقات الكبيرة التي لديها العديد من مؤشرات الترابط, فالنافذة المكدسات المتوازية يمكنها عرض مكدسات الاستدعاءات فقط من أجل مؤشرات الترابط التي تم وضع علامة عليها, و على شريط الأدوات, انقر فوق الزر إظهار العلامات فقط الموجود بجانب مربع القائمة.

    نافذة المكدسات الفارغة وتلميح الأداة

    بعد ذلك، في النافذة مؤشرات الترابط، قم بالإشارة على مؤشرات الترابط واحدة تلو الأخرى لمعرفة كيفية ظهور مكدسات الاستدعاءات الخاصة بهم في النافذة المكدسات المتوازية , و استخدم القائمة المختصرة أو الخلية الأولى لمؤشر ترابط لوضع علامة على مؤشرات الترابط, و انقر مرة أخرى فوق زر شريط الأدوات إظهار العلامات فقط لإظهار كافة مؤشرات الترابط.

لاستئناف التنفيذ حتى نقطة التوقف الثالثة

  1. على القائمة تصحيح قم بالنقر فوق متابعة لاستئناف التنفيذ حتى دخول نقطة التوقف الثالثة

    يظهر الأسلوب في مربعات مختلفة عندما تكون مؤشرات الترابط المتعددة في الأسلوب نفسه ولكن هذا الأسلوب لم يكن في بداية مكدس الاستدعاءات, و إن S.L مثال في نقطة التوقف الحالية الذي يحتوي على ثلاثة مؤشرات ترابط و يظهر في ثلاثة مربعات, فقم بالنقر المزدوج على S.L.

    مسار تنفيذ المكدسات المتوازية

    لاحظ أن S.L باللون الغامق في المربعين الآخرين بحيث يمكنك مشاهدة الأماكن الأخرى التي يظهر فيها, و إذا أردت مشاهدة أي الإطارات تم استدعاؤها في S.L و أي الإطارات التي قام S.L باستدعائها بنفسه, قم بالنقر فوق الزر تبديل عرض الأسلوب الموجود على شريط الأدوات. يبين الرسم التوضيحي التالي طريقة عرض الأسلوب للنافذة المكدسات المتوازية.

    نافذة المكدسات في طريقة عرض الأسلوب

    لاحظ كيفية تَمَحْوُر المخطط في الأسلوب الذي تم تحديده وتمركزه في المربع الخاص به في منتصف العرض, حيث يظهر callees و المتصلين في الجزء العلوي و السفلي, فانقر مرة أخرى فوق الزر تبديل عرض الأسلوب لترك هذا الوضع.

    القائمة المختصرة للنافذة المكدسات المتوازية تحتوي أيضاً على العناصر الأخرى التالية

    • عرض ست عشرية تبديل الأرقام في تلميحات الأدوات بين العشري و السداسي العشري.

    • تقوم معلومات تحميل الرمز و إعدادات الرمز بفتح مربعات الحوار الخاصة.

    • تقوم الانتقال إلى التعليمات البرمجية المصدر و الانتقال إلى التفكيك بالتنقل في محرر الأسلوب المحدد.

    • تقوم إظهار التعليمات البرمجية الخارجية بعرض كافة الإطارات حتى إذا لم تكن موجودة في التعليمات البرمجية للمستخدم. جربه لمشاهدة توسيع المخطط ليتوافق مع الإطارات الإضافية (التي قد تكون خافتة لأنه ليس لديك الرموز الخاصة بها).

    قد ترغب في أن يمرر العرض تلقائياً إلى إطار المكدس النشط لمؤشر الترابط الحالي; أي مؤشر الترابط الذي دخل في نقطة الترابط الأولى, عندما يكون لديك مخططات كبيرة و أنت تنتقل لنقطة التوقف التالية . في النافذة المكدسات المتوازية, تأكد من أن الزر تمرير تلقائي إلى إطار المكدس الحالي الموجود على شريط الأدوات قد تم تشغيله.

    التمرير التلقائي في نافذة المكدسات المتوازية

  2. قبل المتابعة, في النافذة المكدسات المتوازية على طول الطريق للأسفل قم بالتمرير إلى اليسار.

لاستئناف التنفيذ حتى نقطة التوقف الرابعة

  1. لاستئناف التنفيذ حتى يتم إدخال نقطة التوقف الرابعة على القائمة تصحيح, انقر فوق متابعة.

    لاحظ كيفية تمرير العرض تلقائياً في المكان, و قم بالتبديل بين مؤشرات الترابط في النافذة مؤشرات الترابط أو قم بالتبديل بين إطارات المكدس في النافذة مكدس الاستدعاءات, و لاحظ كيفية تمرير العرض تلقائياً إلى الإطار الصحيح دائماً ثم قم بإيقاف تشغيل خيار تمرير تلقائي إلى إطار الأداة الحالي و اعرض الفرق.

    كما تقوم عرض العين للمغطس الخاص بالمساعدة في المخططات الكبيرة الموجودة في النافذة المكدسات المتوازية. فيمكنك مشاهدة عرض العين للمغطس الخاص بالنقر على الزر الموجود بين أشرطة التمرير على الركن الأيمن السفلي للنافذة, كما هو موضح في الرسم التوضيحي التالي.

    نافذة المكدسات المتوازية بطريقة عرض من أعلى

    يمكنك نقل المستطيل للتنقل بسرعة حول المخطط.

    هناك طريقة أخرى لنقل المخطط في أي اتجاه وهي أن تقوم بالنقر فوق مساحة فارغة من المخطط ثم قم بسحبه أينما تريد

    لتكبير و تصغير المخطط, قم بالضغط المستمر على زر المفتاح 'Ctrl' أثناء تحريك عجلة الماوس, وبدلاً من ذلك، انقر فوق الزر تكبير/تصغير الموجود على شريط الأدوات ثم استخدم الأداة تكبير/تصغير .

    تكبير المكدسات وتصغيرها جنبًا إلى جنب

    يمكنك أيضاً عرض المكدسات في الاتجاه من أعلى إلى أسفل بدلاً من أسفل إلى أعلى, وذلك بالنقر فوق القائمة أدوات ثم النقر فوق خيارات و من ثم قم بتحديد أو إلغاء الخيار المتضمن في العقدة تصحيح الأخطاء .

  2. قبل المتابعة, على القائمة تصحيح قم بالنقر فوق إيقاف التصحيح لإنهاء التنفيذ.

باستخدام نافذة المهام المتوازية و عرض المهام لنافذة المكدسات المتوازية

ننصح بإكمال الإجراءات السابقة قبل المتابعة.

لإعادة تعيين التطبيق حتى دخول نقطة التوقف الأولى

  1. على القائمة تصحيح انقر فوق بدء التصحيح ثم انتظر دخول نقطة التوقف الأولى

  2. على القائمة تصحيح, قم بالتأشير على نوافذ ثم انقر فوق مؤشرات الترابط. إرساء النافذة مؤشرات الترابط في الجزء السفلي من ‏‫Visual Studio.

  3. على القائمة تصحيح, قم بالإشارة إلى نوافذ ثم انقر فوق مكدسات الاستدعاءات. إرساء النافذة مكدسات الاستدعاءات في الجزء السفلي من ‏‫Visual Studio.

  4. يقوم بالنقر المزدوج على النافذة مؤشرات الترابط لتحديثها. مؤشرات الترابط الحالية لديها السهم الأصفر. عندما تقوم بتغيير مؤشر الترابط الحالي, فإنه يتم تحديث النوافذ الأخرى. بعد ذلك، فإننا سوف نقوم باختبار المهام.

  5. على القائمة تصحيح, قم بالإشارة إلى Windows ثم انقر فوق المهام المتوازية. يبين الرسم التوضيحي التالي النافذة المهام المتوازية.

    نافذة مهام متوازية بها 4 مهام مشغلة

    بالنسبة لكل مهمة تشغيل يمكنك قراءة هويتها التي يتم إرجاعها بواسطة الخاصية المسماة نفسها, وإن موقع هوية و اسم مؤشر الترابط الذي يقوم بتشغيل المهمة هو: (المرور فوق الذي يقوم بعرض تلميح الادوات الذي يحتوي على كافة مكدس الاستدعاءات). أيضاً، ضمن العمود مهمة، يمكنك مشاهدة الأسلوب الذي تم تمريره إلى المهمة أو بعبارة أخرى; نقطة البداية.

    يمكنك فرز أي عمود. لاحظ الصورة الرمزية المفرزة التي تشير إلى العمود المفرز و الاتجاه. يمكنك أيضاً إعادة ترتيب الأعمدة عن طريق سحبها إلى اليسار أو إلى اليمين.

    يشير السهم الأصفر إلى المكدس الحالي. يمكنك تبديل المهام عن طريق النقر المزدوج على مهمة أو باستخدام القائمة المختصرة. عند تبديل المهام يصبح مؤشر الترابط الأساسي حالي و يتم تحديث النوافذ الأخرى.

    عندما تقوم بالتبديل بين مهمة وأخرى يدوياً, فإن السهم الأصفر يتحرك لكن ما يزال هناك سهم أبيض يظهر المهمة التي تسبب في فصل المصحح.

لاستئناف التنفيذ حتى نقطة التوقف الثانية

  • لاستئناف التنفيذ حتى يتم إدخال نقطة التوقف الثانية على القائمة تصحيح, انقر فوق متابعة.

    سابقاً، العمود حالة قام بإظهار كافة المهام على أنها قيد التشغيل لكن الآن هناك مهمتان قيد الانتظار يمكن أن يتم حظر المهام لأسباب مختلفة. في العمود حالة، قم بالمرور عبر مهمة قيد الانتظار لمعرفة سبب الحظر. فعلى سبيل المثال، في الرسم التوضيحي التالي فإن المهمة 3 في انتظار المهمة 4.

    نافذة مهام متوازية بها مهمتان قيد الانتظار

    المهمة 4 بدورها تنتظر على جهاز عرض مملوك من قبل مؤشر الترابط الذي تم تعيينه إلى المهمة 2.

    نافذة المهام بها مهمة قيد الانتظار وتلميح أداة

    يمكنك وضع علامة على مهمة عن طريق النقر فوق العلامة في العمود الأول للنافذة المكدسات المتوازية.

    يمكنك استخدام وضع علامة لتعقب المهام بين نقاط التوقف المختلفة في جلسة التصحيح نفسها أو لتصفية المهام ذات مكدسات الاستدعاءات التي تظهر في النافذة المكدسات المتوازية.

    عندما تقوم باستخدام النافذة السابقة المكدسات المتوازية، يتم عرض مؤشرات الترابط للتطبيق. قم مرة أخرى بعرض النافذة المكدسات المتوازية, لكن هذه المرة قم بعرض مهام التطبيق. قم بذلك عن طريق تحديد المهام في المربع الموجود على الزاوية العلوية اليسرى. يبين الرسم التوضيحي التالي "طريقة عرض المهام".

    نافذة المهام المتوازية في طريقة عرض المهام

    مؤشرات الترابط التي لا تقوم بتنفيذ المهام حالياً لا يتم عرضها في "طريقة عرض المهام" من النافذة المكدسات المتوازية. أيضاً، للحصول على مؤشرات الترابط التي تنفذ المهام, فإن بعض إطارات المكدس التي لا صلة لها بالمهام يتم تصفيتها من أعلى و أسفل المكدس.

    قم مرة أخرى بعرض النافذة المهام المتوازية. قم بالنقر المزدوج على أي عنوان للعمود لمشاهدة قائمة مختصرة للعمود.

    قائمة رأس عمود المهام المتوازية

    يمكنك استخدام القائمة المختصرة لإضافة الأعمدة أو إزالتها. على سبيل المثال، العمود AppDomain لم يتم تحديده فلذلك لا يعرض في القائمة. انقر فوق الأصل. العمود الأصل يظهر من غير قيم لأي من المهام الأربعة.

لاستئناف التنفيذ حتى نقطة التوقف الثالثة

  • على القائمة تصحيح قم بالنقر فوق متابعة لاستئناف التنفيذ حتى دخول نقطة التوقف الثالثة

    و الآن مهمة 5 الجديدة قيد التشغيل, لكن مهمة 4 قيد الانتظار يمكنك أن ترى لماذا بواسطة المرور عبر المهمة التي هي قيد الانتظار في النافذة حالة في العمود الأصل ، لاحظ أنه المهمة 4 هي الأصل لمهمة 5.

    لتمثيل مرئي أفضل لعلاقة الفرع الأصلي, انقر بزر الماوس الأيمن على عنوان العمود الأصل و من ثم قم بالنقر فوق عرض الفرع الأصلي. يجب عليك مراجعة الرسم التوضيحي التالي

    طريقة عرض المهام المتوازية في طريقة عرض أصلي فرعي

    لاحظ أن المهمة 4 و المهمة 5 قيد التشغيل على مؤشر الترابط نفسه لا يتم عرض هذه المعلومات في النافذة مؤشرات الترابط, فمشاهدتها هنا تعتبر فائدة أخرى للنافذة المهام المتوازية. لتأكيد ذلك قم بعرض النافذة المكدسات المتوازية. تأكد من أنك تقوم بعرض المهام. حدد المهام 4 و 5 بالنقر المزدوج فوقها في النافذة المهام المتوازية. عندما تقوم بتنفيذ ذلك، فإن التمييز الأزرق في النافذة المكدسات المتوازية قد تم تحديثه. يمكنك أيضاً تحديد المهام 4 و 5 بواسطة المسح الضوئي لتلميحات الأدوات على النافذة المكدسات المتوازية.

    نافذة المكدسات المتوازية في طريقة عرض المهام

    في النافذة المكدسات المتوازية، انقر بالزر الأيمن للماوس على S.P و من ثم انقر فوق انتقل إلى مؤشر الترابط. تقوم النافذة بالتبديل إلى "عرض مؤشرات الترابط" و إن الإطار المقابل موجود في العرض. يمكنك مشاهدة كل المهام على مؤشر الترابط نفسه.

    طريقة عرض مؤشرات الترابط مع تمييز مؤشر ترابط

    هذه فائدة أخرى من فوائد "طريقة عرض المهام" في النافذة المكدسات المتوازية التي تم مقارنتها مع النافذة مؤشرات الترابط.

لاستئناف التنفيذ حتى نقطة التوقف الرابعة

  • على القائمة تصحيح قم بالنقر فوق متابعة لاستئناف التنفيذ حتى دخول نقطة التوقف الثالثة انقر فوق عنوان العمود المعرف للفرز على حسب المعرّف. يجب عليك مراجعة الرسم التوضيحي التالي

    مكدسات متوازية بها مهام في أربعة حالات

    لأنه تم اكتمال مهمة 5 التي لن يتم عرضها إذا كان هذا لا يحدث على جهاز الكمبيوتر الخاص بك, و لا يتم إظهار حالة التوقف التام فانتقل مرة واحد بالضغط على F11.

    إن مهمة 3 و مهمة 4 الآن تنتظر بعضها البعض التي هي في حالة التوقف التام. هناك أيضاً خمسة مهام جديدة التي هي عبارة عن أفرع المهمة 2 المجدولة الآن. المهام المجدولة هي المهام التي قد بدأت في التعليمات البرمجية لكنها لم تعمل بعد. ولذلك، فإن الأعمدة الموقع و مؤشر الترابط المعين فارغة.

    قم مرة أخرى بعرض النافذة المهام المتوازية. يحتوي عنوان كل مربع على تلميح الأدوات الذي يوضح معرفات و أسماء مؤشر الترابط. قم بالتبديل إلى "طريقة عرض المهام" في النافذة المكدسات المتوازية. قم بالمرور فوق رأس الصفحة لمشاهدة معرف المهمة و الاسم و حالة المهمة ، كما هو موضح في الرسم التوضيحي التالي.

    نافذة المكدسات المتوازية مع تلميح شاشة عنوان

    يمكنك تجميع المهام حسب العمود. في النافذة المهام المتوازية, انقر بزر الماوس الأيمن على عنوان العمود حالة ثم انقر فوقمجموعة حسب الحالة. يبين الرسم التوضيحي التالي النافذة المهام المتوازية التي تم تجيمعها على حسب الحالة.

    نافذة مهام متوازية بها مجموعة مهام

    يمكنك أيضاً التجميع بواسطة أي عمود آخر. يمكنك التركيز على مجموعة فرعية من مهام بواسطة تجميع المهام. تحتوي كل مجموعة قابلة للطي عدد من العناصر التي تم تجميعها معاً. يمكنك أيضاً الإشارة بشكلٍ سريع على العناصر في المجموعة بالنقر فوق الزر يؤشر إلى يمين الزر طي.

    نافذة مهام متوازية مجمّعة

    الميزة الأخيرة للنافذة المهام المتوازية لفحص القائمة المختصرة التي يتم عرضها عند النقر بزر الماوس الأيمن فوق مهمة.

    قائمة السياق الموسعة لنافذة "المهام المتوازية"

    القائمة المختصرة تقوم بعرض أوامر مختلفة استناداً إلى حالة المهمة. قد تتضمن الأوامر نسخ و تحديد الكل و عرض ست عشرية و تحويل إلى المهمة و تجميد مؤشر ترابط معين و تجميد كافة مؤشرات الترابط عدا هذه و إلغاء مؤشر ترابط معين و يؤشر.

    يمكنك تجميد مؤشر الترابط الأساسي لمهمة أو لمهام أو يمكنك تجميد كافة مؤشرات الترابط باستثناء تلك المخصصة. يتم تمثيل مؤشر الترابط المجمد في النافذة المهام المتوازية كما في النافذة مؤشرات الترابط باستخدام أيقونة زرقاء إيقاف مؤقت.

ملخص

تُظهر هذه المعاينة مصحح النوافذ المهام المتوازية و المكدسات المتوازية, فاستخدم هذه النافذات في مشاريع حقيقية تستخدم التعليمات البرمجية ذات مؤشرات الترابط المتعددة, و يمكنك فحص التعليمات البرمجية المتوازية المكتوبة في C++ أو C# أو Visual Basic.

راجع أيضًا:

المهام

الإرشادات التفصيلية: تصحيح الأخطاء لتطبيق متوازي

المبادئ

البرمجة المتوازية في .NET Framework

وقت تشغيل التزامن

موارد أخرى

تخطيط المصحح

تصحيح تعليمات برمجية مُدارة

باستخدام نافذة المكدسات المتوازية:

باستخدام نافذة المهام المتوازي