2011-09-11 36 views
6

Nếu tôi có một IUnknown *ptr, tôi có cần phải gọi Release() trên tất cả các giao diện tôi có được thông qua ptr->QueryInterface(), ngoài để gọi ptr->Release() khi tôi đang thực hiện với ptr?IUnknown :: QueryInterface() tăng số lượng tham chiếu?

Tôi đã từng nghĩ rằng câu trả lời là "Có", nhưng this quote from MSDN nhầm lẫn tôi:

Đôi khi bạn có thể cần để có được một tài liệu tham khảo yếu đến một đối tượng (có nghĩa là, bạn có thể muốn có được một con trỏ với một trong các giao diện của nó mà không cần tăng số lượng tham chiếu), nhưng không thể chấp nhận điều này bằng cách gọi QueryInterface theo sau là Release.

Tôi không hiểu tại sao đó là vấn đề - nếu tôi gọi ptr->QueryInterface() và sau đó gọi Release trên con trỏ kết quả, nên không phải là tính tham khảo trên các đối tượng vẫn là tích cực? Làm thế nào mà kết quả trong một con trỏ không hợp lệ?

Trả lời

6

Các tài liệu là chính xác. Và bạn cần thực hiện theo các quy tắc đếm tham chiếu - bao gồm gọi Release trên các giao diện thu được từ QueryInterface ngoài sau khi bạn đã tạo đối tượng.

Để giải thích lý do tại sao bạn không thể làm con trỏ yếu với Release - có tồn tại tình trạng cuộc gọi trong khi gọi QueryInterface và sau đó Release ngay sau đó.

  • thread1 tạo đối tượng - tài liệu tham khảo số 1
  • thread2 gọi QueryInterface để tham khảo yếu - tính tham khảo đối tượng 2
  • thread1 ấn phẩm - tính tham khảo 1
  • thread2 gọi Release để tham khảo yếu - tài liệu tham khảo số 0. Đối tượng bị phá hủy.
  • Thread2 cố gắng sử dụng đối tượng - lỗi.

Cảnh báo là có để bảo vệ chống lại bên trên - có lẽ một số lập trình viên nghĩ rằng họ có thể "gọi ptr->QueryInterface() và sau đó gọi Release trên con trỏ kết quả" và sau đó sử dụng các đối tượng ...

+0

Ahh ... vì vậy đó là do luồng, tôi đã không nhận ra điều đó. Cảm ơn! +1 – Mehrdad

+2

Thậm chí không phân luồng, nếu bạn 'Release' của bạn * mạnh * (chúng ta hãy gọi nó) tham chiếu trước * yếu * một sau đó tham chiếu * yếu * không còn giá trị. Gọi 'Release' về cơ bản là cách nói của bạn" okay tôi đã làm "- một khi bạn gọi nó, ngừng sử dụng đối tượng. Nếu bạn cần tiếp tục sử dụng đối tượng ... đừng gọi 'Release'. –

+0

Phải; Tôi chỉ tự hỏi về trường hợp bạn gọi là 'Release' trên giao diện mới * ngay lập tức sau cuộc gọi 'QueryInterface' của bạn, và vì vậy đó là lý do tại sao tôi không nghĩ đến một điều kiện chủng tộc. – Mehrdad

4

IUnknown :: QueryInterface Phương pháp

Lấy con trỏ đến các giao diện hỗ trợ trên một đối tượng.

Phương thức này gọi IUnknown :: AddRef trên con trỏ nó trả về.

Trực tiếp từ tài liệu tham khảo IUnknown :: QueryInterface tại http://msdn.microsoft.com/en-us/library/ms682521%28v=vs.85%29.aspx

2

Theading không phải là kịch bản duy nhất; Tôi muốn đi xa như vậy để nói rằng luồng không thực sự là kịch bản chính ở tất cả: các quy tắc COM ngày trở lại Win16 trước khi đa luồng preemptive đã được thêm vào Windows ở nơi đầu tiên.

Vấn đề chính là khi COM được quan tâm, số lượng tham chiếu là trên mỗi giao diện là , không phải trên đối tượng. Việc triển khai COM là miễn phí để thực thi một tham chiếu bằng cách thực hiện nó cho mỗi đối tượng - đó có lẽ là cách đơn giản nhất để thực hiện nó trong C++, đặc biệt khi đối tượng COM ánh xạ tới một đối tượng C++. và mã máy khách COM không thể dựa vào trường hợp đó.

Có nhiều đối tượng COM trên mạng có thể tạo ra các giao diện khi đang chạy theo yêu cầu và sau đó tiêu diệt chúng ngay sau khi chúng không còn cần thiết nữa. Trong những trường hợp đó, nếu bạn gọi QI để có được một trong các giao diện này, khi bạn gọi Release, bộ nhớ cho giao diện đó có thể được dealloated, vì vậy việc sử dụng nó có thể dẫn đến lỗi/crash/etc.

Nói chung, bạn phải coi mọi cuộc gọi đến -> Release() là có khả năng giải phóng bộ nhớ đằng sau con trỏ.

(Ngoài ra, lưu ý rằng COM không thực sự có một khái niệm về tài liệu tham khảo yếu để bắt đầu với: có được tính() tài liệu tham khảo mạnh mẽ, và đó là nó.)

+0

Đoán ban đầu của tôi cũng là số lượng tham chiếu là mỗi giao diện, nhưng tôi không nghĩ rằng thực sự có thể hapen, vì quy tắc COM nói rằng [gọi 'QueryInterface' trên' IUnknown' hai lần ** phải ** trả lại * * cùng một con trỏ vật lý] (http://msdn.microsoft.com/en-us/library/ms682521.aspx). Vì vậy, thực sự, có vẻ như nó * có * là cùng một đối tượng mỗi lần. – Mehrdad

+2

Địa điểm _only_ trong đó COM yêu cầu cùng một giá trị con trỏ là nơi bạn QI cho IUnknown cụ thể; đây là một trường hợp đặc biệt được gọi là IUnknown kinh điển. Tất cả các giao diện khác có thể được tạo theo yêu cầu. (Nó thực sự là trường hợp này mà _allows_ các giao diện khác được theo yêu cầu, và vẫn có một số khả năng để so sánh danh tính đối tượng, bằng cách so sánh Canonical IUnknown. xem http://msdn.microsoft.com/en-us/library/wh8b86c9(v=vs.80).aspx) – BrendanMcK

0

Các gợi ý để tránh tài liệu tham khảo yếu không giải quyết cuộc đua vấn đề.

T1 operator new, create object, references: 1 
T1  passes interface object reference to T2, thinking it can "share" ownership 
T1  suspends 
T2  resumes 
T2 QueryInterface 
T2  suspends before InterlockedIncrement, references: 1 
T1  resumes 
T1 Calls Release 
T1  suspends between InterlockedDecrement and operator delete, references: 0 
T2  resumes, InterlockedIncrement occurs, references 1 
T2  suspends 
T1  resumes, operator delete executes, references 1 !!! 
T1  suspends 
T2  resumes 
T2 Any reference to the interface is now invalid since it has been deleted with reference count 1. 

Đây là khả năng giải quyết trong máy chủ COM. Tuy nhiên, máy khách COM không nên phụ thuộc vào máy chủ ngăn chặn tình trạng chủng tộc này. Do đó, COM clients KHÔNG PHẢI chia sẻ các đối tượng giao diện giữa các luồng. Chủ đề duy nhất được phép truy cập đối tượng giao diện, là chuỗi ONE hiện đang "sở hữu" đối tượng giao diện.

T1 KHÔNG được gọi là Bản phát hành. Có, nó có thể được gọi là AddRef trước khi truyền đối tượng giao diện cho T2. Nhưng điều đó có thể không giải quyết được cuộc đua, chỉ di chuyển nó ở đâu đó khác. Cách tốt nhất là luôn duy trì khái niệm về một đối tượng giao diện, một chủ sở hữu.

Nếu máy chủ COM muốn hỗ trợ khái niệm hai (hoặc nhiều hơn) giao diện có thể tham khảo một số trạng thái nội bộ của máy chủ được chia sẻ, máy chủ COM nên quảng cáo hợp đồng bằng cách cung cấp phương thức CreateCopyOfInstance và quản lý tranh chấp, nội bộ. Có, tất nhiên, ví dụ về các máy chủ xử lý loại "fan-out" này. Hãy xem các giao diện lưu trữ liên tục từ Microsoft. Ở đó, các giao diện KHÔNG "gây ra" .. mỗi giao diện nên được sở hữu bởi một người dùng đơn lẻ (thread/process/whatever) và "fan-out" được quản lý, nội bộ, bởi máy chủ, với các phương thức được cung cấp cho COM khách hàng để kiểm soát một số khía cạnh của các vấn đề tranh chấp. Do đó, các máy chủ COM của Microsoft phải giải quyết các điều kiện cuộc đua như một phần hợp đồng của họ với khách hàng của họ.

Các vấn đề liên quan