방법: 백그라운드 스레드에서 UML 모델 업데이트

경우에 따라 백그라운드 스레드에서 모델을 변경하면 유용할 때가 있습니다. 예를 들어 속도가 느린 외부 리소스에서 정보를 로드하는 경우 백그라운드 스레드를 사용하여 업데이트를 감독할 수 있습니다. 이렇게 하면 각 업데이트가 발생할 때마다 사용자가 바로 확인할 수 있습니다.

그러나 UML 저장소는 스레드로부터 안전하지 않다는 사실을 잘 알고 있어야 합니다. 따라서 다음과 같은 사전 조치가 중요합니다.

  • 모델이나 다이어그램에 대한 모든 업데이트는 UI(사용자 인터페이스) 스레드에서 수행되어야 합니다. 백그라운드 스레드는 Invoke 또는 Invoke``1를 사용하여 UI 스레드에서 실제 업데이트를 수행하도록 해야 합니다.

  • 일련의 변경 내용을 단일 트랜잭션으로 그룹화하는 경우 트랜잭션이 진행되는 동안 사용자가 모델을 편집할 수 없도록 하는 것이 좋습니다. 그렇지 않으면 사용자가 편집한 모든 내용이 같은 트랜잭션의 일부로 포함됩니다. 모달 대화 상자를 표시하여 사용자가 변경할 수 없도록 할 수 있습니다. 원하는 경우 대화 상자에 취소 단추를 제공할 수 있습니다. 사용자가는 변경하는 즉시 내용을 확인할 수 있습니다.

예제

이 예제에서는 백그라운드 스레드를 사용하여 모델에서 몇 가지 내용을 변경합니다. 여기에서는 스레드가 실행되는 동안 사용자를 제외하기 위해 대화 상자가 사용됩니다. 간단한 이 예제에서는 대화 상자에 취소 단추가 제공되지 않습니다. 그러나 해당 기능을 쉽게 추가할 수 있습니다.

예제를 실행하려면

  1. 방법: 모델링 다이어그램의 메뉴 명령 정의에서 설명한 대로 C# 프로젝트에 명령 처리기를 만듭니다.

  2. 프로젝트에 다음과 같은 어셈블리에 대한 참조가 포함되도록 합니다.

    • Microsoft.VisualStudio.ArchitectureTools.Extensibility

    • Microsoft.VisualStudio.Modeling.Sdk.12.0

    • Microsoft.VisualStudio.Modeling.Sdk.Diagrams.12.0

    • Microsoft.VisualStudio.Uml.Interfaces

    • System.ComponentModel.Composition

    • System.Windows.Forms

  3. ProgressForm이라는 Windows form을 프로젝트에 추가합니다. 이 폼에는 업데이트가 진행되고 있음을 나타내는 메시지가 표시되어야 하며, 다른 컨트롤은 포함할 필요가 없습니다.

  4. 7단계 이후에 표시된 코드를 포함하는 C# 파일을 추가합니다.

  5. 프로젝트를 빌드하고 실행합니다.

    Visual Studio의 새 인스턴스가 실험 모드에서 시작됩니다.

  6. 실험적 Visual Studio의 인스턴스에서 UML 클래스 다이어그램을 만들거나 엽니다.

  7. UML 클래스 다이어그램의 아무 곳이나 마우스 오른쪽 단추로 클릭하고 여러 UML 클래스 추가를 클릭합니다.

0.5초의 간격으로 몇 개의 새 클래스 상자가 차례로 다이어그램에 나타납니다.

using System;
using System.ComponentModel;
using System.ComponentModel.Composition;
using System.Threading;
using System.Windows.Forms;

using Microsoft.VisualStudio.ArchitectureTools.Extensibility.Presentation;
using Microsoft.VisualStudio.ArchitectureTools.Extensibility.Uml;
using Microsoft.VisualStudio.Modeling.ExtensionEnablement;
using Microsoft.VisualStudio.Uml.Classes;

namespace BackgroundThreadProgressUI // CHANGE TO YOUR NAMESPACE
{
  [Export(typeof(ICommandExtension))]
  [ClassDesignerExtension]
  class UmlClassAdderCommand : ICommandExtension
  {

    [Import]
    IDiagramContext context { get; set; }

    [Import]
    ILinkedUndoContext linkedUndoContext { get; set; }

    // Called when the user runs the command.
    public void Execute(IMenuCommand command)
    {
      // The form that will exclude the user.
      ProgressForm form = new ProgressForm();

      // System.ComponentModel.BackgroundWorker is a
      // convenient way to run a background thread.
      BackgroundWorker worker = new BackgroundWorker();
      worker.WorkerSupportsCancellation = true;

      worker.DoWork += delegate(object sender, DoWorkEventArgs args)
      {
        // This block will be executed in a background thread.

        IClassDiagram diagram = context.CurrentDiagram as IClassDiagram;
        IModelStore store = diagram.ModelStore;
        const int CLASSES_TO_CREATE = 15;

        // Group all the changes together.
        using (ILinkedUndoTransaction transaction = linkedUndoContext.BeginTransaction("Background Updates"))
        {
          for (int i = 1; i < CLASSES_TO_CREATE; i++)
          {
            if (worker.CancellationPending) 
               return; // No commit - undo all.

            // Create model elements using the UI thread by using
            // the Invoke method on the progress form. Always 
            // modify the model and diagrams from a UI thread.
            form.Invoke((MethodInvoker)(delegate
            {
              IClass newClass = store.Root.CreateClass();
              newClass.Name = string.Format("NewClass{0}", i);
              diagram.Display(newClass);
            }));
            

            // Sleep briefly so that we can watch the updates.
            Thread.Sleep(500);
          }
          
          // Commit the transaction or it will be rolled back.
          transaction.Commit();
        }
      };

      // Close the form when the thread completes.
      worker.RunWorkerCompleted += delegate(object sender, RunWorkerCompletedEventArgs args)
      {
        form.Close();
      };

      // Start the thread before showing the modal progress dialog.
      worker.RunWorkerAsync();

      // Show the form modally, parented on VS.
      // Prevents the user from making changes while in progress.
      form.ShowDialog();
    }

    public void QueryStatus(IMenuCommand command)
    {
      command.Enabled = command.Visible = true;
    }

    public string Text
    {
      get { return "Add several classes"; }
    }
  }
}

사용자가 예제에서 스레드를 취소할 수 있도록 하려면

  1. 진행률 대화 상자에 취소 단추를 추가합니다.

  2. 진행률 대화 상자에 다음 코드를 추가합니다.

    public event MethodInvoker Cancel;

    private void CancelButton_Click(object sender, EventArgs e)

    {

    Cancel();

    }

  3. Execute() 메서드에서 폼 생성 뒤에 다음 줄을 삽입합니다.

    form.Cancel += delegate() { worker.CancelAsync(); };

UI 스레드 액세스를 위한 다른 메서드

대화 상자를 만들지 않으려면 다음과 같이 다이어그램을 표시하는 컨트롤에 액세스할 수 있습니다.

DiagramView uiThreadHolder = context.CurrentDiagram.GetObject<Diagram>().ActiveDiagramView;

uiThreadHolder.Invoke()를 사용하여 UI 스레드에서 작업을 수행할 수 있습니다.

참고 항목

개념

방법: 모델링 다이어그램의 메뉴 명령 정의

방법: 모델링 다이어그램의 제스처 처리기 정의