2016-10-23 1 views
1

BST에 여러 요소를 삽입하는 작업이 있습니다. 여러 스레드를 사용하여 작업을 최적화해야합니다. 실행될 수있는 스레드 수에는 제한이 없습니다.BST (Binary Search Tree)의 다중 스레드 삽입

내 접근 방식입니다. 이것은 이론적 인 접근 방법입니다. 나는 그것을 구현하려고 시도하지 않았으며 그것이 어느 정도 효과가 있을지 전혀 모른다. 이 아이디어에 대해 의견을 제시하십시오.

BST 노드는 다음과 같이 보일 것이다 : 나는 오히려 잠금 상태를 표시하기 위해 두 개의 부울 변수를 사용하여, 자바 잠금을 사용하고 있지 않다

class BSTNode { 
    int val; 
    BSTNode left, right; 
    boolean leftLock, rightLock; 
    Queue<BSTNode> leftQ, rightQ; 
} 

.

먼저 모든 요소를 ​​삽입하면 관련 위치를 찾는 것이 필요하므로이 작업은 트리를 수정하지 않으므로 병렬로 수행 할 수 있습니다. 작업은 경로상의 노드가 잠금 해제 될 때까지만 작동합니다. 특정 노드가 잠겨 있으면 해당 삽입 스레드는 sleep()이되고 해당 왼쪽 또는 오른쪽 대기열에 추가되고 잠금이 해제되면 다시 작동합니다.

경로의 노드가 하나도 없으면 삽입을 계속할 수 있습니다. 부모의 해당 포인터를 삽입하고 수정하기 전에 부모에서 잠금을 획득해야합니다.

누구나이 구현 방법에 대한 의견을 제시 할 수 있습니까?

답변

1

이것은 실제로 자체 잠금을 구현하는 데 사용되는 연습입니다. 당신이 한 일은 언팩 된 잠금 (boolean, waitingQueue)이 생성됩니다. 그러나이 접근 방식이 안전하게 작동하는 유일한 방법은 외부에서 '잠금'부울 변수 및 대기열에 대한 액세스를 동기화하는 경우입니다. 따라서이 비 잠금 코드를 성공적으로 작동 시키려면 잠금 장치를 사용해야합니다. 당신이 잠금을 사용하지 않은 경우

당신은 동시성에 관한 몇 가지 문제가있을 것입니다 :

  • 이 전혀 발생-전에 노드의 값의 설정 사이의 관계를. 즉, 다른 스레드 중 어느 것도 필드의 업데이트 된 값을 볼 수 없습니다. 이것은 혼자서 모든 종류의 문제를 일으킬 수 있습니다. 그러나 구체적인 예가 더 있습니다.
  • 스레드가 값을 변경했거나 다른 스레드가 값을 변경 (경쟁 조건)했기 때문에 부울 잠금의 할당이되었는지 여부를 알 수 없습니다. 본질적으로, 아무 스레드도 자물쇠를 소유하고 있는지 여부를 알 수 없습니다. 내장 클래스를 사용하여이 문제를 해결할 수 있지만 해결해야 할 가치가없는 다른 문제가 충분히 있습니다.
  • 잠금을 확인하고 자신을 대기열에 삽입하는 사이에 또 ​​다른 경쟁 조건이 있습니다. 한 스레드는 다른 스레드가 잠금을 가졌음을 알 수 있으며 (두 번째 점이 주어지면 모호함) 자체를 대기열에 추가합니다. 그러나 대기열에 자신을 추가 할 때까지 잠금은 잠금 해제 될 수 있으며 다른 스레드가 트리의 해당 부분을 만지면 무한 시간을 기다릴 수 있습니다.
  • 성능이 좋지 않습니다. 각 스레드는 한 번에 노드에서만 볼 수 있습니다. 부울/대기열 구조를 잠금으로 변환하더라도 트리에서 search() 유형의 작업조차도 잠금을 사용하여 올바른 메모리 가시성 및 발생 가능성을 보장하기 때문에 성능이 좋지 않을 가능성이 높습니다.당신이 하위 선형 검색 시간이 스레드 안전, 주문, 변경 가능한 컨테이너를 원하는 경우

는 ConcurrentSkipListSet < 정수 >

1

흥미로운 문제를 사용합니다. 그리고 접근법을 재정의해야하는 몇 가지 사항이 있습니다.

모든 요소의 삽입 먼저 관련 위치를 찾기 위해 우리를 필요로하기 때문에

,이 작업은 사실이 잘못
  • 병렬

    수행 될 수있다. 트리를 수정하지 않을 것이지만이 트리 (노드 삽입)를 수정하려는 백그라운드의 스레드가 있으므로 여기에 잠금/세마포어를 적용해야합니다.
  • 그리고 finding a suitable place + actual insertion을 한 번의 작업으로 수행해야합니다. insert. 그 이유는 하나의 스레드 (예 : t1)가 finding a suitable place을 마친 다음 actual insertion을 시도했지만 다른 스레드 (예 : t2)가 actual insertion을 수행 중이므로 대기해야하는 경우 첫 번째 스레드 (t1)가 두 번째 스레드 (t2) actual insertion 다음에 트리가 변경되었으므로 위치 계산을 다시 수행하십시오. 이진 검색 트리 삽입 다른 삽입 독립적으로 수행 할 수 없기 때문에,

그래서 결론적으로, 이진 검색에 대한 병렬 삽입 트리 당신을 도움이되지 것이다 (나는 당신이 무슨 말 것 같아) .

+0

요소를 삽입 한 후에 트리가 변경되지만 새로운 검색은 루트가 아닌 특정 노드에서 시작할 수 있다는 것을 알고 있습니다. 이것은 엄청난 수의 요소를 삽입 할 때 시간에 영향을줍니다. 위치 찾기 작업은 경로의 첫 번째 노드가 잠길 때까지만 병렬 처리됩니다. 루트의 다른면에 삽입 할 값은 여전히 ​​병렬로 검색 할 수 있습니다. p 스레드의 'p'시간 성능을 기대하지는 않지만 순차적 인 것보다 좋을 것입니다. – LearningToCode

+0

@LearningToCode는 새로운 검색이 특정 노드에서 시작될 수 있다는 것과 일치하며 동시에 상당한 수의 스레드에 적용될 수 없습니다. 그래서 유일한 편리한 솔루션은 처음부터 시작될 것입니다. 그렇지 않으면 계속하기 위해 여러 노드를 전환하는 시간을 낭비하고 있습니다. 두 번째 요점이 맞습니다. 그 영향을 분석 할 시간을주십시오. :)) –

1

이 접근법에서 기본적인 허점을 보여주는 문제 만 설명하려고합니다.

지금은 삽입 조작 만 지원하고 다른 조작은 지원하지 않는다고 가정하십시오. 삽입 작업의 구현 될 수있다 다음은 마지막 노드 (잎 노드)의 아이들이 값을 삽입하고 업데이 트하기 때문에,이 방법에서

//Using C 
BSTNode* insert(BSTNode* root,int value) 
{ 
1 if(root == NULL){ 
2  return createNewNode(value); 
3 } 
4  
5 if(root->data == value){ 
6  return root; 
7 } 
8 else if(root->data > value){ 
9  while(root->leftLock); 
10  if(!root->left){ 
11   root->leftLock = true; 
12   root->left = insert(root->left,value); 
13   root->leftLock = false; 
14  } 
15  else{ 
16   root->left = insert(root->left,value); 
17  } 
18 } 
19 else{ 
20  while(root->rightLock); 
21  if(!root->right){ 
22   root->rightLock = true; 
23   root->right = insert(root->right,value); 
24   root->rightLock = false; 
25  } 
26  else{ 
27   root->right = insert(root->right,value); 
28  } 
29 } 
30  
31 return root; 
32  
} 

, 그래서 업데이트하는 동안 우리는 어떤 잠금을 수행하지 않는 부모 (때 다시 되풀이).

나는 삽입 요청 대기열을 피하고 스핀 록을 사용하여 조금만 유지하려고한다. 내가 거 인상이 너무 경우에 동일합니다 오전 포인트 ...

이 BST을 고려 그러나 :

10 
/\ 
    5 15 
/\/\ 
2 6 13 20 

은 가정하자 2 개 스레드 T1과 T2는 동시에 각각의 값 (25, 26)를 삽입하려고 호출 현재 값이 20 인(맨 오른쪽 노드) 인 BSTNode에 있습니다.

a. t1: 
      1. if(root == NULL) //not true, will go to line 5. 
      //switch 

b. t2: 
      1. if(root == NULL) //not true, will go to line 5. 
      //switch 

c. t1: 
      5. if(root->data == value){ //not true, will go to line 8. 
      8. else if(root->data > value) //not true, will go to line 19. 
      //switch 

d. t2: 
      5. if(root->data == value){ //not true, will go to line 8. 
      //switch 

e. t1: 
      19 else{ 
      20  while(root->rightLock); // lock is not held by anyone, so continue. 
      21  if(!root->right){ 
      //switch 
f. t2: 
      8. else if(root->data > value) //not true, will go to line 19. 
      19 else{ 
      20  while(root->rightLock); // lock is not helpd by anyone, so continue. 
      21  if(!root->right){ 
      22   root->rightLock = true; 
      //switch 

g. t1: 
      22   root->rightLock = true; 
      23   root->right = insert(root->right,value); 
      //switch 

h. t2: 
      23   root->right = insert(root->right,value); 
      24   root->rightLock = false; 
      //switch 

23은 그 라인의 완전한 실행을 덮는 라인을 가정

지금 스레드 문맥을 전환하여 상기 코드를 실행할 수 있습니다.

하면 부 F, g 모두 T1 및 T2 서로의 존재를 알지 못하고 임계 영역에 진입하고있다H에서 볼 수 있듯이. 코드가이를 허용하지 않았습니다.

무엇이 문제입니까?

문제는 한 번에 실행되도록했는데 코드 조각 있다는 것입니다 :

20  while(root->rightLock); 
21  if(!root->right){ 
22   root->rightLock = true; 

그래서 우리는 3 작업을 실행하는 우리 자신의 무정전 지시를함으로써 일부 하드웨어 제어를 필요로 할 수는 함께 위에서 언급 한.

+1

이것은 훌륭한 대답입니다. 나는 두 스레드가 어떻게 제한된 영역으로 나아 갔는지 설명했다. – slashdottir