Another option is to code the cleaning logic in a timer job.
Create a timer job that runs daily/weekly/monthly (night hours), loop through the tasks list and delete orphan tasks and tasks of canceled workflows.
1.) Delete orphan tasks
Tasks created by workflows started on list items have field ‘WorkflowListId’ that contains the Guid of the list and field ‘WorkflowItemId’ that contains the ID of the associated list item.
Tasks created otherwise (not by list workflows) have null in ‘WorkflowListId’ and ‘WorkflowItemId’.
You can use these two fields to get only the workflow tasks and if there is no item with that ID in the list (the item has been deleted) then delete the task.
2.) Delete tasks of terminated workflows
You can get the workflow status and if it is stoped by the user then delete the task.
int id = Convert.ToInt32(taskItem[SPBuiltInFieldId.WorkflowItemId]);
SPListItem item = list.GetItemById(id);
string workflowInstanceId = taskItem[SPBuiltInFieldId.WorkflowInstanceID].ToString();
SPWorkflow workflow = item.Workflows[new Guid(workflowInstanceId)];
If (workflow.StatusValue = SPWorkflowStatus.StoppedByUser)